Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace underscores with hyphens in Perl Catalyst URLs

Tags:

perl

catalyst

We're looking at options for converting CamelCase to camel-case and underscores to hyphens and hoping someone can offer some strong examples. (Using hyphens for SEO reasons).

Specifically:

Working on an MMORPG in Catalyst and getting tired of having to write things like this:

sub travel_to  ( $self, $c, $star ) : Path('travel-to') Args(1)  { ... }

Or this:

package Veure::Controller::Area::StorageLocker {
    ....
    sub index ( $self, $c ) : Path('/area/storage-locker') { ... }
}

Update: Because there's some confusion, I meant that we'd much prefer to have devs write this:

# getting rid of Args() would be nice, but sigs don't have introspection
sub travel_to  ( $self, $c, $star ) : Args(1)  { ... }

Or this:

package Veure::Controller::Area::StorageLocker {
    ....
    sub index ( $self, $c ) { ... }
}

This is important because for an SEO standpoint, underscores instead of hyphens can dramatically improve your SEO. By having to do extra grunt work to always force hyphens, developers are forgetting to do this and we keep wasting money going back and having to rewrite code where this caveat was forgotten. This is the sort of thing which we should be able to do automatically.

like image 978
Ovid Avatar asked Jun 22 '16 12:06

Ovid


Video Answer


2 Answers

I did a bit of digging in the Catalyst sources.

Cammel case controller names

You can modify class2prefix in Catalyst::Utils to change how the controller names translate to the namespace.

Here is a very quick hack that demonstrates what is going on with a fresh MyApp created with catalyst.pl. I borrowed Borodin's suggestion to implement it.

package MyApp::Controller::FooBar;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }

{
    require Class::Method::Modifiers;
    require String::CamelCase;

    Class::Method::Modifiers::around(
        'Catalyst::Utils::class2prefix'  => sub {
            my $orig = shift;

            # I borrowed most of this from the original function ...
            my $class = shift || '';
            my $prefix = shift || 0;
            if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
                $prefix = $2;
                $prefix =~ s{::}{/}g;

                # ... and this from https://stackoverflow.com/a/37968830/1331451
                $prefix = String::CamelCase::decamelize($prefix) =~ tr/_/-/r;
            }

            return $prefix;
        }
    );
}

sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    $c->response->body('Matched MyApp::Controller::FooBar in FooBar.');
} 

1;

I tested this briefly, but cannot guarantee it's not going to break anything else. I believe if it's put into a better place and done in a more appropriate way it could be a viable option.

Underscores in actions

This one looks trickier. My best bet is to fiddle with Catalyst::DispatchType::Path in some way, or create something that installs an ActionClass that modifies it. It's basically replacing the _ with a -. That thing could be built around gather_default_action_roles in Catalyst::Controller (maybe as a subclass) to add that one to all actions. This is highly speculative.

like image 140
simbabque Avatar answered Nov 15 '22 13:11

simbabque


CPAN has the String::CamelCase module, which offers a decamelize function, after which you will need to convert underscores to hyphens using tr/_/-/

I hope this short example helps to answer your question

use strict;
use warnings 'all';
use v.14.1;

use String::CamelCase 'decamelize';

for my $s ( 'travel_to', 'Veure::Controller::Area::StorageLocker' ) {
    (my $ss = $s) =~ s|^[\w:]*::Controller(?=::)||;
    $ss =~ s|::|/|g;
    $ss = decamelize($ss) =~ tr/_/-/r;
    say $ss;
}

output

travel-to
/area/storage-locker
like image 39
Borodin Avatar answered Nov 15 '22 12:11

Borodin