2 boas técnicas para refatorar controladores Mojolicious

Vamos começar com este código feio.

sub grow {
my ($self) = @_;

my $moustache = $self->app->moustaches->create({});

$self
->res->code(303);
$self
->redirect_to( 'show_moustache', id => $moustache->id );
}

sub show {
my ($self) = @_;

my $moustache = $self->app->moustaches->find( $self->param('id') );
if (!$moustache) {
$self
->render_not_found;
}

$self
->stash( moustache => $moustache );
}

sub trim {
my ($self) = @_;

my $moustache = $self->app->moustaches->find( $self->param('id') );
if (!$moustache) {
$self
->render_not_found;
}

$moustache
->trim;

$self
->res->code(204);
}

sub shave {
my ($self) = @_;

my $moustache = $self->app->moustaches->find( $self->param('id') );
if (!$moustache) {
$self
->render_not_found;
}

$moustache
->delete;

$self
->res->code(204);
}

A primeira refatoração agradável que podemos fazer aqui é definir um atributo com um retorno de chamada do construtor lento.

has 'moustache' => sub {
my ($self) = @_;
return $self->app->moustaches->find( $self->param('id') );
};

sub grow {
my ($self) = @_;

$self
->moustache( $self->app->moustaches->create({}) );

$self
->res->code(303);
$self
->redirect_to( 'show_moustache', id => $self->moustache->id );
}

sub show {
my ($self) = @_;

if ( !$self->moustache ) {
$self
->render_not_found;
}

$self
->stash( moustache => $self->moustache );
}

sub trim {
my ($self) = @_;

if ( !$self->moustache ) {
$self
->render_not_found;
}

$self
->moustache->trim;

$self
->res->code(204);
}

sub shave {
my ($self) = @_;

if ( !$self->moustache ) {
$self
->render_not_found;
}

$self
->moustache->delete;

$self
->res->code(204);
}

O próximo candidato óbvio para refatoração é a verificação de existência, mas não há nenhum análogo de Rails before_filterno Mojolious. Felizmente, há um bom módulo Class::Method::Modifiers, que é usado Moo, aliás.

use Class::Method::Modifiers;

has
'moustache' => sub {
my ($self) = @_;
return $self->app->moustaches->find( $self->param('id') );
};

around
[ qw{ show trim shave } ] => &_assert_moustache;

sub grow {
my ($self) = @_;

$self
->moustache( $self->app->moustaches->create({}) );

$self
->res->code(303);
$self
->redirect_to( 'show_moustache', id => $self->moustache->id );
}

sub show {
my ($self) = @_;
$self
->stash( moustache => $self->moustache );
}

sub trim {
my ($self) = @_;

$self
->moustache->trim;
$self
->res->code(204);
}

sub shave {
my ($self) = @_;

$self
->moustache->delete;
$self
->res->code(204);
}

sub _assert_moustache {
my $orig = shift;
my $self = shift;

if ( !$self->moustache ) {
$self
->render_not_found;
}

return $orig->($self);
}

Note que para poder impedir a execução do método, ao qual aplicamos um filtro, precisamos de usar around, porque beforenão o permite.