I need to be able to write and call my own subroutines in a Mojolicious::Lite Application. However, the intuitive way to do this doesn't seem to be working. I emailed a colleague who has more Mojolicious experience than I do with this question and he sent me the following code:
#!/usr/bin/env perl
use Mojolicious::Lite;
# Documentation browser under "/perldoc"
plugin 'PODRenderer';
get '/' => sub {
my $self = shift;
$self->render('index');
};
sub factorial {
my $n = shift;
return $n ? $n * factorial($n - 1) : 1;
}
app->start;
__DATA__
@@ index.html.ep
% layout 'default';
% title 'Welcome';
Welcome to the Mojolicious real-time web framework!
Five factorial: <%= main::factorial(5) %>
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body><%= content %></body>
</html>
But when I run this it tells me that when I'm calling an undefined subroutine:
Undefined subroutine &main::factorial called at template index.html.ep from DATA section line 5, line 32.
I've spent time mucking around with this code, and trying different things that will get it to work, but so far the only thing that makes it run properly is when the subroutine is defined within the scope of the @@ xxx.html.ep's. I've googled/searched stackoverflow for "user defined subroutines in Mojolicious::Lite" and other similar queries. Nothing seems to come up. My searches of the documentation have proved fruitless as well. This seems like it should be a simple task, but I'm a bit stuck. Any help would be appreciated.
As PerC has already mentioned, helpers are the preferred mechanism for adding behaviors (or access to behaviors) to templates. Since he has already shown that example, I will just add that you can make a hybrid of the two by doing something like this (I'm removing the pod renderer plugin, you don't need it).
#!/usr/bin/env perl
use Mojolicious::Lite;
get '/' => sub {
my $self = shift;
$self->render('index');
};
sub factorial {
my $n = shift;
return $n ? $n * factorial($n - 1) : 1;
}
helper factorial => sub { shift; factorial(@_) };
app->start;
__DATA__
@@ index.html.ep
% layout 'default';
% title 'Welcome';
Welcome to the Mojolicious real-time web framework!
Five factorial: <%= factorial(5) %>
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body><%= content %></body>
</html>
If you re-work the the sub into a helper it can be accessed from within a template (or controller).
#!/usr/bin/env perl
use Mojolicious::Lite;
helper factorial => sub {
my ($self, $n) = @_;
return $n ? $n * $self->factorial($n - 1) : 1;
};
get '/' => 'index';
app->start;
__DATA__
@@ index.html.ep
Five factorial: <%= factorial(5) %>
Running the app gives (minus the logging):
$ perl app.pl get /
Five factorial: 120
$
Update
This will neither answer your question, but gives you another option.
I don't know the background why you prefer to call the sub in main from the template. In my opinion the template (or view I you like) should only present data, and not try to calculate/process data, that's the job for the model (or controller). As the same (and more) data is available in the controller as in the template, why don't you call the sub in the controller and just pass the result to the template? The result can be whatever Perl data structure so you aren't limited to scalars.
Additionally when you grow the Mojolicious::Lite
app into a full app, there is no
longer a useful package main
(it's just a small application that calls your main package MyApp
). That makes the whole idea of calling generic subs from templates even less a way forward.
Here is another example of the factorial app, where the calculation is done in the controller:
#!/usr/bin/env perl
use Mojolicious::Lite;
sub factorial {
my $n = shift;
return $n ? $n * factorial($n - 1) : 1;
}
get '/' => sub {
my $self = shift;
$self->render('index', result => factorial(5));
};
app->start;
__DATA__
@@ index.html.ep
Five factorial: <%= $result %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With