Can't call method "context" on an undefined value - perl

I am trying to call subroutines from one controller file to another when I am writing the following code:
Abc.pm This is the file I have the code that I need to call a subroutine to another controller file. The following subroutine I need to call.
package MyApp::Controller::Abc;
use Moose;
use IO::File;
use Data::Dumper;
use MyApp::MyConfig;
use MyApp::DateUtils;
use MyApp::Arrs::API;
use MyApp::Constants;
use namespace::autoclean;
sub get_token_id :Private
{
my $self = shift;
my $c = $self->context;
my $myDBI = $c->model('MyDBI')->new;
return $myDBI->get_token_id;
}
The above code I need to call to Def.pm file. Now I am calling as following:
package MyApp::Controller::Def;
use Moose;
use namespace::autoclean;
use MyApp::Utils;
BEGIN { extends 'Catalyst::Controller'; }
my($self, $c) = #_;
my ($State, $Zip, $Country) = #_;
my $tokenid = $self->get_token_id;
I am getting the following error:
Can't call method "get_token_id" on an undefined value
But I need to call as following only:
When I am using the following code:
package MyApp::Controller::Def;
use Moose;
use namespace::autoclean;
use MyApp::Utils;
BEGIN { extends 'Catalyst::Controller'; }
my $self = shift;
my $c = $self->context;
my ($State, $Zip, $Country) = #_;
my $coid = $self->get_token_id;
I am getting this error:
Can't call method "context" on an undefined value
Can any one help me why I am getting this error.
Thanks in advance...

The root cause here appears to be that you're not instantiating your objects properly.
Using:
$self = shift;
is an object oriented notation, and it makes no sense if you're doing it outside a subroutine - which is what appears to be happening here. And more specifically - a subroutine that's called as a method, using $object -> subname($some_parameter);. If you do this, then perl passes a reference to the object as the first argument to the subroutine - which is where things like:
sub my_method {
my $self = shift;
$self -> {some_attribute} = 1;
$self -> some_other_method(#args);
}
or
sub some_other_method {
my ( $self, #args ) = #_;
foreach ( #args ) {
print;
}
}
type notation kicks in.
You're not doing this - you're 'shifting' in the body of a module, which will have no #_ it's undefined, and then you're trying to call a context method within an undefined object. Hence the error. get_token_id has the same root cause.
I can't easily offer advice on how to fix it, because it's hard to be sure what you're actually trying to do. I would suggest reviewing how OO perl works though, as a refresher might be beneficial.

Related

Catching undefined value accessing in Perl

How can I catch access to member variables?
$Class1->{Class2}
If the Class2 field doesn't exist, is is possible to catch this from an internal function?
You can, but you probably shouldn't. The problem here is - if you access a variable within a class directly... then you just can. You can prevent this with a couple of workarounds - and this is where things like Moose come in.
And there's a couple of slightly hacky tricks like inside-out objects (which I think aren't common practice any more - Perl Best Practice advocated them some years back) or using anonymous hashes to hold state.
But failing that - why not use an accessor, and auto-generate one using 'AUTOLOAD'.
#!/usr/bin/env perl
package MyClass;
use strict;
use warnings;
use vars '$AUTOLOAD';
sub AUTOLOAD {
my ( $self ) = #_;
my $subname = $AUTOLOAD =~ s/.*:://r;
if ( $self -> {$subname} ) {
return $self -> {$subname};
}
warn "Sub called $subname was called\n";
return "$subname";
}
sub new {
my ( $class ) = #_;
my $self = {};
bless $self, $class;
}
package main;
use strict;
use warnings;
my $object = MyClass -> new;
$object -> {var} = "fleeg";
print "Undef fiddle was: ", $object -> fiddle,"\n";
print "But 'var' was: ", $object -> var,"\n";
This has the same problem, in that changing method names might cause things to break. However it has the advantage that you can handle 'invalid' method calls however you like.
But really - explicit 'get' and 'set' methods are better choices for most use-cases.
You do this by providing proper getter/setter methods that wrap around your class/instance variables. The internals should never be accessed directly, particularly from outside of the class itself (it's wise to not do so within the class either, except for the actual method that maintains that specific attribute. Here's a very basic example:
use warnings;
use strict;
package A;
sub new {
my ($class, %args) = #_;
my $self = bless {}, $class;
$self->x($args{x});
$self->y($args{y});
return $self;
}
sub x {
my ($self, $x) = #_;
$self->{x} = $x if defined $x;
return $self->{x} // 1;
}
sub y {
my ($self, $y) = #_;
$self->{y} = $y if defined $y;
return $self->{y} // 2;
}
package main;
my $obj = A->new(x => 5, y => 3);
print $obj->x ."\n";
print $obj->y ."\n";
Now, you could just as easily do print $obj->{x}, but that's where your problem is. What happens when the code is much more complicated than this, and for some reason you want to change the x attribute name to foo, but retain the x() method? $obj->{x} will now be undef as its never set.
Always use the provided methods for accessing attributes of a class/object. Encapsulation such as this is a staple of OO programming.

Getting wrong argument value when trying to call a subroutine using PackageName::ModuleName->subroutine("xyz")in perl

When trying to call a subroutine (defined in a user defined module) in other perl file, getting wrong argument value
#moduleName.pm
package PackageName::moduleName;
use strict;
use warnings;
use base 'Exporter';
sub callMe{
my($readArg)=(#_);
print $readArg;
}
#test.pl
use strict;
use warnings;
use FindBin; # locate this script
use lib 'path to parent directory'; # use the parent directory
use PackageName::moduleName;
if( my $temp=PackageName::moduleName->callMe("test")){
print" True : $temp\n";
}
The function prints value of $temp as : PackageName::moduleName
Not able to figure out why.
P.S. I have to maintain same convention while calling the subroutine
You are calling a function as a class method with Foo::Bar->frobnicate(#args). In that case, Perl will do the following things because of the arrow ->:
check what's on the left of the arrow
if it's blessed, find the package (e.g. $q is package CGI)
if it's not blessed, assume it's a package (e.g. Foo::Bar)
within that package namespace, find a sub with the name on the right of the arrow (e.g. frobnicate)
call that sub and pass what's on the left of the arrow as the first argument
Now it looks like this:
Foo::Bar::frobnicate('Foo::Bar', #args);
In frobnicate, you have to deal with that:
sub frobnicate {
my ($class, #args) = #_;
# ...
}
That's typically done in a new, which is the most likely use of a class method.
If you don't want to deal with it, call the sub directly in its namespace, and not with the arrow notation.
my $rv = Foo::Bar::frobnicate(#args);
Because of the way you're calling it via ->.
When you do this, perl passes extra arguments, so you can make a constructor (new).
E.g.
my $thing = Package::Module -> new();
The first argument passed is the class, so you can use that for a bless. See:
perlootut
E.g.
sub new {
my ( $class, #args ) = #_;
my $self = {};
bless ( $self, $class );
}
This also applies when you call an instantiated object:
$thing -> do_something();
It passes a reference to $self as the first argument.
sub do_something {
my ( $self, #args ) = #_;
print "Got args of #args\n";
$self -> {firstarg} = shift ( #args );
}
If you want to do that, try instead:
PackageName::ModuleName::callMe("test");

Using Perl's Method::Signatures, why can't I invoke methods on an object instance?

I followed what friedo said here.
Now, when I try to call the method testScript I get the error global symbol $obj requires explicit package name and it fails to call testScriptTwo.
use strict;
use warnings;
package Test;
use Method::Signatures;
method new {
my $obj = bless {}, $self;
return $obj;
}
method testScript {
$obj->testScriptTwo(); # Error happens here
}
method testScriptTwo { ... }
Test script:
use Test;
my $class = Test->new();
$class->testScript();
How do I make use of $obj to call methods within the package itself?
Use this instead:
method testScript {
$self->testScriptTwo();
}
The first argument is in the variable $self, not $obj
Your questions seem to indicate you do not understand the basics of scope, and how plain Perl objects work.
In Perl, when you use the ->method syntax on a package name or blessed reference, the subroutine method in that package is invoked. The first argument to the subroutine is the thing on which you invoked method.
So, if you do
My::Friend->new('Alfred');
the new subroutine in the package My::Friend receives two arguments. My::Friend and Alfred.
In a new method, it is customary to refer to the first argument as $class, but that is completely up to you. You could use $basket_case if you were so inclined:
sub new {
my $basket_case = shift;
my $basket = shift;
my $obj = bless { name => $basket } => $basket_case;
return $obj;
}
If you then invoke a method on the returned reference, that method will receive said reference as its first argument, allowing you to access data stored in that reference:
sub blurb {
my $schmorp = shift;
print $schmorp->{name}, "\n";
return;
}
Putting it all together:
#!/usr/bin/env perl
package My::Package;
use strict;
use warnings;
sub new {
my $basket_case = shift;
my $basket = shift;
my $obj = bless { name => $basket } => $basket_case;
return $obj;
}
sub blurb {
my $schmorp = shift;
print $schmorp->{name}, "\n";
return;
}
sub derp {
my $herp = shift;
printf "%s derp derp\n", $herp->{name};
return;
}
package main;
my $x = My::Package->new('Alfred');
$x->blurb;
$x->derp;
Output:
Alfred
Alfred derp derp
You need to understand these basics. Trying to put another layer of abstraction on top of the basics before understanding what is underneath will not make things any easier.
Now, if you are using Method::Signatures, it, by convention, puts that implicit first argument in a lexically scoped variable which, by default, it calls $self.
You can override that name in specific methods, and doing so in new might be a good idea to convey the fact that it doesn't expect an object instance; instead it returns a new instance.
Whatever you called that lexically scoped instance variable in one sub does not affect what it is called in another sub. For example:
#!/usr/bin/env perl
use strict;
use warnings;
sub a_number {
my $number = int(rand(10));
return $number;
}
sub square_that_number {
my $x = shift;
return $x * $x;
}
my $bzzzt = a_number();
my $trrrp = square_that_number($bzzzt);
print $trrrp, "\n";
Output:
$ ./zt.pl
36
OK, you need to backtrack a bit - you're new method is broken in the first place, which indicates that you don't really understand what's going on with OO perl.
A very simple object looks like this:
package Foo;
sub new {
#when Foo -> new is called, then 'Foo' is passed in as the class name
my ( $class ) = #_;
#create an empty hash reference - can be anything, but $self is the convention
my $self = {};
#tell perl that $self is a 'Foo' object
bless ( $self, $class );
#return the reference to your `Foo` object
return $self;
}
sub set_name {
my ( $self, $new_name ) = #_;
$self -> {name} = $new_name;
}
sub get_name {
my ( $self ) = #_;
return $self -> {name};
}
When you call this in your code:
use Foo;
my $new_instance = Foo -> new();
The class is passed into the new method, which you then use bless to create an instantiated object.
Then you can 'do stuff' with it - when you 'call' a method using -> then the first argument into the subroutine is the object reference.
So
$new_instance -> set_name ( "myname" );
print $new_instance -> get_name();
Is equivalent to:
Foo::set_name($new_instance, "myname" );
print Foo::get_name($new_instance);
You act on $new_instance which is a sort of magic hash that allows you to include code.
Method::Signatures is largely irrelevant until you understand the basics of OO. But what that does is 'simply' expand the functions within a module, such that you don't have to extract self/class etc.
By default, a method defined as method provides $self automatically. no $obj like you're using. That's a variable that's local to you new method, and simply doesn't exist outside that.

How can I use Perl's File::Find inside a Moose Object?

I'm building a Perl application that relies on Moose. One task the Moose object needs to accomplish is to use File::Find to populate an attribute with a list of files. I'm having some trouble figuring out how to use find's \&wanted code reference in a way that will let me maintain access to a $self version of the Moose object.
So far, I have this:
#!/usr/bin/perl
package MyMoose;
use Modern::Perl;
use Moose;
use File::Find;
use FindBin qw($Bin);
### Attribute to hold the file list
has "file_list" => (
is => 'rw',
isa => 'ArrayRef',
default => sub {[]}
);
### Method to populate the file list
sub update_file_list {
my $self = shift;
find(\&wanted, $Bin);
}
### File::Find wanted sub
sub wanted {
### This won't work, but shows what I'd like to do
# my $self = shift;
# ### Do some filtering
# push #{$self->file_list}, $File::Find::name;
}
1;
######################################################################
### Main package to test the object.
package main;
use Data::Dumper;
run_main() unless caller();
sub run_main {
my $m = MyMoose->new();
$m->update_file_list();
print Dumper $m->file_list;
}
It runs, but obviously doesn't assemble a file list. That's the part I'm trying to figure out.
What's the proper way to use File::Find so that it will let you have access to the Moose object during processing?
The problem is that you don't have access to $self within wanted sub. You can use inline closure and default or builder to build the list.
Edit: updated code per updated question
has "file_list" => (
is => 'rw',
isa => 'ArrayRef',
default => sub {
my $self = shift;
return $self->_get_file_list();
},
);
sub update_file_list {
my $self = shift;
$self->file_list($self->_get_file_list());
}
sub _get_file_list {
my #files;
find(sub { push #files, $File::Find::name }, $Bin);
return \#files;
}
_get_file_list method returns arrayref of files found. It is used both in default and update_file_list method to populate the attribute.
As bvr notes, the subroutine reference passed to find doesn't need to be a named package method — a lexical closure will work just fine. Thus, you can do this:
sub update_file_list {
my $self = shift;
my $wanted = sub {
### Do some filtering
push #{$self->file_list}, $File::Find::name;
};
find($wanted, $Bin);
}
The lexical variable $self declared in the outer function scope will be visible in the inner function.
In particular, every time the update_file_list method is called, a new $self and a new $wanted will be created (and bound together by the inner reference to $self), so that it's perfectly safe to call the method several times on different objects, even recursively if you want.
After some trial an error, I also got the script to work by replacing the original version of 'update_file_list' with this:
sub update_file_list {
my $self = shift;
find( sub { wanted($self); }, $Bin );
}
That seems to work as well.

How does one get a method reference when using Moose

I'm trying to figure out how to get a method code reference using Moose.
Below is an example of what I'm trying to do:
use Modern::Perl;
package Storage;
use Moose;
sub batch_store {
my ($self, $data) = #_;
... store $data ...
}
package Parser;
use Moose;
has 'generic_batch_store' => ( isa => 'CodeRef' );
sub parse {
my $self = shift;
my #buf;
... incredibly complex parsing code ...
$self->generic_batch_store(\#buf);
}
package main;
$s = Storage->new;
$p = Parser->new;
$p->generic_batch_store(\&{$s->batch_store});
$p->parse;
exit;
The question I linked to above goes into detail about the various options when encapsulating a method call in a code ref. In your case, I would write the main package as:
my $storage = Storage->new;
my $parser = Parser->new;
$parser->generic_batch_store(sub {$storage->batch_store(#_)});
$parser->parse;
$storage is changed to a lexical so that the code reference sub {$storage->batch_store(#_)} can close over it. The (#_) added to the end allows arguments to be passed to the method.
I am not a Moose expert, but I believe that you will need to call the code with an additional dereferencing arrow:
$self->generic_batch_store->(\#buf);
which is just shorthand for:
($self->generic_batch_store())->(\#buf);