Search code examples
perlmojoliciousmojolicious-lite

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.


Solution

  • 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.