gpwr gpwr - 2 months ago 14
Perl Question

Mojolicious: Can't call method "render" on an undefined value

I'm getting this error and cannot understand why this happens. It happens when I jump to another subroutine. Perhaps there is something I need to understand about Mojolicious on why this happens.

Here is the source code of my program:

#!/usr/bin/perl

use Mojolicious::Lite;

get '/' => sub { &start_home; };

app->start;

sub start_home {
my $d = shift;
my $something = $d->param('something');
### Do things with $something.... etc.. etc..
&go_somewhere_else; ### Go somewhere else
}

sub go_somewhere_else {
my $c = shift;
$c->render(text => "Hello World!");
### End of program
}


I am passing a value on to the renderer and there is a value - Why would it say it is undefined? My understanding is that this only happens if you jump to a subroutine and try to render output.

My operating system is Windows and I am using Strawberry Perl.

Answer

You need to pass the context object $c/$d to your second function. The undefined value is your $c in go_somewhere_else, because you call it without a parameter.

Initially, to make it work, do this.

sub start_home {
  my $d = shift;
  my $something = $d->param('something');

  go_somewhere_else($d);
}

You are now passing the context, which you named $d (that's not the conventional name), to the other function, and the warning will go away.

That's because the form &subname; without parenthesis () makes @_ (that's the list of arguments to the function) available inside of go_somewhere_else, but because you shifted $d off, @_ is now empty, and hence your $c inside go_somewhere_else is undef.

Alternatively, you could also change the shift to an assignment with @_. But please, don't do that!

sub start_home {
  my ( $d ) = @_;
  my $something = $d->param('something');

  &go_somewhere_else;
}

There are more things odd to the point of almost wrong here.

get '/' => sub { &start_home; };

You are currying the the start_home function, but you are not actually adding another parameter. I explained above why this works. But it's not great. In fact, it's confusing and complicated.

Instead, you should use a code reference for the route.

get '/' => \&start_home;

Inside of start_home, you should call your context $c as is the convention. You should also not use the ampersand & notation for calling functions. That changes the behavior in a way you most certainly do not want.

sub start_home {
  my $c = shift;
  my $something = $c->param('something');

  # ...
  go_somewhere_else($c);
}

To learn more about how function calls work in Perl, refer to perlsub.