the URL '/user/1' should dynamically point and rewrite to '/user/John-H' in the browser address bar
By this I'm assuming you mean redirect?
and in link mouseover on the page
This is a little bit unclear: if you're generating the pages yourself, then it would be easiest to just insert the correct link in the first place, like I did in the code below. Otherwise, if your HTML really contains a link like <a href="/user/1"> and you want it to appear differently on mouseover, you'll have to solve that with JavaScript.
I have mapped id to value in the 'users' cache.
This seems a bit strange: a Mojo::Cache, being a cache, is transient, so I assume you're not actually using that as your data store in your actual application (and if you are, you shouldn't). You may have oversimplified your question a bit, since it's unclear what data source you're getting user IDs and names from, like a database? Anyway, I've just used a dummy.
restrict the value of id to be an even number and less than 10.
That's done simply enough with a regex of [02468], but since I suspect in your actual application you probably don't want to limit it to numbers below 10, in the following code I just limited it to any even integer.
Also note that your description is not self-consistent. You want /user/1 to rewrite to /user/John-H, but you also want to restrict IDs to even numbers. It's also unclear if/how you want to map 'John H' to 'John-H' and back, and how you want to handle '/user/NonexistentUser'.
Anyway, here's my best guess:
#!/usr/bin/perl
use Mojolicious::Lite -signatures;
helper users => sub ($c) { # probably a database in reality?
state $users => {
2 => 'John H',
4 => 'Sam A',
} };
get '/' => sub ($c) { $c->render(template => 'index') } => 'index';
get '/user/:id' => [id => qr/\d*[02468]/] => sub ($c) {
my $id = $c->param('id');
if ( exists app->users->{$id} )
{ $c->redirect_to('user_by_name', name=>app->users->{$id} ) }
else { $c->reply->not_found }
} => 'user_by_id';
get '/user/:name' => sub ($c) {
# a very inefficient grep here, don't use this in production!!
if ( grep { $_ eq $c->param('name') } values app->users->%* )
{ $c->render( template=>'user' ) }
else { $c->reply->not_found }
} => 'user_by_name';
app->start;
__DATA__
@@ layouts/main.html.ep
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body>
%= content
</body>
</html>
@@ index.html.ep
% layout 'main', title => 'Hello, World!';
<p>User List</p>
<ul>
% for my $id (sort {$a<=>$b} keys users->%*) {
<li>
%= link_to "User ID $id" => user_by_id => { id => $id }
%= link_to users->{$id} => user_by_name=>{name=>users->{$id}}
</li>
% }
</ul>
@@ user.html.ep
% layout 'main', title => 'User page';
<p>This is <%= $name %>'s Page</p>
%= link_to Home => 'index';
Tests:
use Mojo::Base -strict, -signatures;
use Test::Mojo;
use Test::More;
use Mojo::File;
use Mojo::Util qw/url_escape/;
my $t = Test::Mojo->new( Mojo::File->new('/path/to/app.pl') );
$t->get_ok('/')->status_is(200)->content_like(qr/\bUser List\b/);
$t->get_ok('/user/1')->status_is(404);
$t->get_ok('/user/2')
->header_is(location => '/user/' . url_escape 'John H');
$t->get_ok('/user/3')->status_is(404);
$t->get_ok('/user/4')
->header_is(location => '/user/' . url_escape 'Sam A');
$t->get_ok('/user/5')->status_is(404);
$t->get_ok('/user/6')->status_is(404);
$t->get_ok('/user/2/') # trailing slash
->header_is(location => '/user/' . url_escape 'John H');
$t->get_ok('/user/John H')->status_is(200)
->content_like(qr/\bThis is John H's Page\b/);
$t->get_ok('/user/Sam A')->status_is(200)
->content_like(qr/\bThis is Sam A's Page\b/);
$t->get_ok('/user/John Doe')->status_is(404);
done_testing;
Minor typo fixes in text.