One of the things we know from years of programming is that you should never hard-code anything if you don't have to. And yet far too many web application hard-code their urls, especially internal ones. But what if you didn't have to?

Each Mojolicious route has its own name which can be used to generate urls. If you don't specify one, one is generated, but you shouldn't rely on that name, give it one that is meaningful and relevant to your purposes. In lite apps, the name is the last parameter, after any defaults or callbacks. (In a full app it is an attribute, but we'll talk about those in another post).

Then when you need a url, rather than hard-coding it, use url_for or related functionality to generate a url by name, you can even pass placeholder values if needed. Let's see how it works!

The North Pole Homepage

Santa started out with a simple webapp. It just had a main page and a page for him and his employees. Since it was so simple, he didn't have any deeper paths, just / for the homepage and /:name for staff pages. Of course what I'm going to show you is a simplification, I can't show you Santa's full site, for obvious reasons.

use Mojolicious::Lite;

get '/:name' => {template => 'staff'} => 'staff';
get '/' => {template => 'home'} => 'home';

app->start;

__DATA__

@@ staff.html.ep
<p>This is <%= ucfirst $name %>.</p>

@@ home.html.ep
<p>Welcome to The North Pole!</p>

<p>
  Say hi to <%= link_to 'Santa' => staff => {name => 'santa'} %>
  and <%= link_to 'Rudolph' => staff => {name => 'rudolph'} %>.
</p>

And we can see that link_to generates link tags using route names. The first argument is the text that will be displayed by the link. The following arguments build the url by name and placeholder value(s). The return is actually a Mojo::URL so if you need to add other things like query parameters, that can be done afterwards.

Changing the Routes

Everything was fine until one day Santa decided that he wanted show off some of the year's new toys too. The problem was that he hadn't planned his urls for that. He could just keep all the existing ones and add a special cased /toys route, but it would look silly that way. What he really needed to do was move all of the staff pages to keep the urls consistent.

use Mojolicious::Lite;

get '/toy/:toy_name' => {template => 'toy'} => 'toy';
get '/meet/:name' => {template => 'staff'} => 'staff';
get '/' => {template => 'home'} => 'home';

app->start;

__DATA__

@@ toy.html.ep
<p>Look at this amazing <%= $toy_name %>.</p>

@@ staff.html.ep
<p>This is <%= ucfirst $name %>.</p>

@@ home.html.ep
<p>Welcome to The North Pole!</p>

<p>
  Say hi to <%= link_to 'Santa' => staff => {name => 'santa'} %>
  and <%= link_to 'Rudolph' => staff => {name => 'rudolph'} %>.
</p>

<p>
  And just wait until you see our amazing
  <%= link_to 'new puzzle' => toy => {toy_name => 'puzzle'} %>!
</p>

Because the staff page urls were generated by name, Santa didn't need to change anything but the route definition!

Route Names as Default Template Names

There is one more thing I should mention. I could have actually made the previous examples with even less code. If a route doesn't explicitly render something, it will check to see if a template exists for the name of the route. It is a shortcut that some people like and could have made the routing definitions look like this:

get '/toy/:toy_name' => 'toy';
get '/meet/:name' => 'staff';
get '/' => 'home';

While some people appreciate that feature, I personally like to be explicit about which template I want. I also like to separate concerns. Anyway, you can choose whichever you like best. After all, there's more than one way to do it!

Image by Travis Wise licensed under the Creative Commons Attribution 2.0 Generic license.

Tagged in : advent, lite, routing

author image
Joel Berger

Joel has Ph.D. in Physics from the University of Illinois at Chicago. He an avid Perl user and author and is a member of the Mojolicious Core Team.