Communitygithub.com

perl-patterns

Idiomes Perl 5.36+, bonnes pratiques et conventions pour construire des applications Perl robustes et maintenables. À utiliser lors de l'écriture ou de la révision de code Perl moderne.

Qu'est-ce que perl-patterns ?

Cette compétence fournit un guide complet des idiomes et bonnes pratiques du Perl moderne (5.36+), destiné aux développeurs écrivant ou révisant du code Perl. Elle couvre les piliers fondamentaux comme le pragma `use v5.36`, les signatures de sous-routines, la sensibilité au contexte, le déréférencement postfixé et l'opérateur `isa`. La gestion d'erreur est détaillée à travers les patterns `eval/die`, Try::Tiny et le `try/catch` natif de Perl 5.40+. L'orientation objet moderne est abordée via Moo (recommandé), Moose si nécessaire, et le mot-clé natif `class` (Corinna, 5.38+). Les expressions régulières, les structures de données, les opérations sur fichiers avec Path::Tiny, l'organisation des modules et les outils de qualité (perltidy, perlcritic, carton) sont également traités. Un tableau de référence rapide fait le lien entre les anciens patterns et leurs équivalents modernes, tandis qu'une section anti-patrons met en garde contre les pratiques dangereuses ou obsolètes. Cette compétence s'applique à tout environnement de développement Perl (CLI, serveur, scripts) et permet d'adopter un style de codage propre, lisible et sûr, en phase avec les évolutions du langage.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/perl-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modèles de Développement Perl Moderne

Idiomes Perl 5.36+ et bonnes pratiques pour construire des applications robustes et maintenables.

Quand Activer

  • Écrire du nouveau code ou des modules Perl
  • Réviser du code Perl pour la conformité aux idiomes
  • Refactoriser du code Perl legacy vers les standards modernes
  • Concevoir l'architecture de modules Perl
  • Migrer du code pré-5.36 vers le Perl moderne

Comment Ça Fonctionne

Appliquer ces modèles comme un biais vers les paramètres par défaut du Perl moderne 5.36+ : signatures, modules explicites, gestion d'erreur ciblée et limites testables. Les exemples ci-dessous sont conçus pour être copiés comme points de départ, puis adaptés à l'application, à la pile de dépendances et au modèle de déploiement que vous avez sous les yeux.

Principes Fondamentaux

1. Utiliser le Pragma v5.36

Un simple use v5.36 remplace l'ancien code standard et active strict, warnings et les signatures de sous-routines.

# Bien : Préambule moderne
use v5.36;

sub saluer($nom) {
    say "Bonjour, $nom !";
}

# Mal : Code standard legacy
use strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';

sub saluer {
    my ($nom) = @_;
    say "Bonjour, $nom !";
}

2. Signatures de Sous-routines

Utiliser les signatures pour la clarté et la vérification automatique du nombre d'arguments.

use v5.36;

# Bien : Signatures avec valeurs par défaut
sub connecter_db($hote, $port = 5432, $timeout = 30) {
    # $hote est obligatoire, les autres ont des valeurs par défaut
    return DBI->connect("dbi:Pg:host=$hote;port=$port", undef, undef, {
        RaiseError => 1,
        PrintError => 0,
    });
}

# Bien : Paramètre slurpy pour arguments variables
sub logger_message($niveau, @details) {
    say "[$niveau] " . join(' ', @details);
}

# Mal : Dépaquetage manuel des arguments
sub connecter_db {
    my ($hote, $port, $timeout) = @_;
    $port    //= 5432;
    $timeout //= 30;
    # ...
}

3. Sensibilité au Contexte

Comprendre le contexte scalaire vs liste — un concept central de Perl.

use v5.36;

my @elements = (1, 2, 3, 4, 5);

my @copie  = @elements;            # Contexte liste : tous les éléments
my $nombre = @elements;            # Contexte scalaire : nombre (5)
say "Éléments : " . scalar @elements; # Forcer le contexte scalaire

4. Déréférencement Postfixé

Utiliser la syntaxe de déréférencement postfixé pour la lisibilité avec des structures imbriquées.

use v5.36;

my $donnees = {
    utilisateurs => [
        { nom => 'Alice', roles => ['admin', 'utilisateur'] },
        { nom => 'Bob',   roles => ['utilisateur'] },
    ],
};

# Bien : Déréférencement postfixé
my @utilisateurs = $donnees->{utilisateurs}->@*;
my @roles = $donnees->{utilisateurs}[0]{roles}->@*;
my %premier = $donnees->{utilisateurs}[0]->%*;

# Mal : Déréférencement circonfixe (plus difficile à lire en chaîne)
my @utilisateurs = @{ $donnees->{utilisateurs} };
my @roles = @{ $donnees->{utilisateurs}[0]{roles} };

5. L'Opérateur isa (5.32+)

Vérification de type infixe — remplace blessed($o) && $o->isa('X').

use v5.36;
if ($obj isa 'Ma::Classe') { $obj->faire_quelque_chose }

Gestion d'Erreur

Pattern eval/die

use v5.36;

sub analyser_config($chemin) {
    my $contenu = eval { path($chemin)->slurp_utf8 };
    die "Erreur de configuration : $@" if $@;
    return decode_json($contenu);
}

Try::Tiny (Gestion d'Exception Fiable)

use v5.36;
use Try::Tiny;

sub recuperer_utilisateur($id) {
    my $utilisateur = try {
        $db->resultset('Utilisateur')->find($id)
            // die "Utilisateur $id introuvable\n";
    }
    catch {
        warn "Échec de la récupération de l'utilisateur $id : $_";
        undef;
    };
    return $utilisateur;
}

try/catch Natif (5.40+)

use v5.40;

sub diviser($x, $y) {
    try {
        die "Division par zéro" if $y == 0;
        return $x / $y;
    }
    catch ($e) {
        warn "Erreur : $e";
        return;
    }
}

POO Moderne avec Moo

Préférer Moo pour une POO légère et moderne. Utiliser Moose uniquement lorsque son métaprotocole est nécessaire.

# Bien : Classe Moo
package Utilisateur;
use Moo;
use Types::Standard qw(Str Int ArrayRef);
use namespace::autoclean;

has nom  => (is => 'ro', isa => Str, required => 1);
has email => (is => 'ro', isa => Str, required => 1);
has age   => (is => 'ro', isa => Int, default  => sub { 0 });
has roles => (is => 'ro', isa => ArrayRef[Str], default => sub { [] });

sub est_admin($self) {
    return grep { $_ eq 'admin' } $self->roles->@*;
}

sub saluer($self) {
    return "Bonjour, je suis " . $self->nom;
}

1;

# Utilisation
my $utilisateur = Utilisateur->new(
    nom   => 'Alice',
    email => '[email protected]',
    roles => ['admin', 'utilisateur'],
);

# Mal : Hashref béni (pas de validation, pas d'accesseurs)
package Utilisateur;
sub new {
    my ($classe, %args) = @_;
    return bless \%args, $classe;
}
sub nom { return $_[0]->{nom} }
1;

Rôles Moo

package Role::Serialisable;
use Moo::Role;
use JSON::MaybeXS qw(encode_json);
requires 'VERS_HASH';
sub vers_json($self) { encode_json($self->VERS_HASH) }
1;

package Utilisateur;
use Moo;
with 'Role::Serialisable';
has nom  => (is => 'ro', required => 1);
has email => (is => 'ro', required => 1);
sub VERS_HASH($self) { { nom => $self->nom, email => $self->email } }
1;

Mot-clé Natif class (5.38+, Corinna)

use v5.38;
use feature 'class';
no warnings 'experimental::class';

class Point {
    field $x :param;
    field $y :param;
    method magnitude() { sqrt($x**2 + $y**2) }
}

my $p = Point->new(x => 3, y => 4);
say $p->magnitude;  # 5

Expressions Régulières

Captures Nommées et Drapeau /x

use v5.36;

# Bien : Captures nommées avec /x pour la lisibilité
my $log_re = qr{
    ^ (?<horodatage> \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} )
    \s+ \[ (?<niveau> \w+ ) \]
    \s+ (?<message> .+ ) $
}x;

if ($ligne =~ $log_re) {
    say "Heure : $+{horodatage}, Niveau : $+{niveau}";
    say "Message : $+{message}";
}

# Mal : Captures positionnelles (difficiles à maintenir)
if ($ligne =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {
    say "Heure : $1, Niveau : $2";
}

Patterns Précompilés

use v5.36;

# Bien : Compiler une fois, utiliser plusieurs fois
my $email_re = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;

sub valider_emails(@emails) {
    return grep { $_ =~ $email_re } @emails;
}

Structures de Données

Références et Accès Profond Sécurisé

use v5.36;

# Références de hachage et de tableau
my $config = {
    base_de_donnees => {
        hote => 'localhost',
        port => 5432,
        options => ['utf8', 'sslmode=require'],
    },
};

# Accès profond sécurisé (retourne undef si un niveau manque)
my $port = $config->{base_de_donnees}{port};           # 5432
my $manquant = $config->{cache}{hote};                 # undef, pas d'erreur

# Tranches de hachage
my %sous_ensemble;
@sous_ensemble{qw(hote port)} = @{$config->{base_de_donnees}}{qw(hote port)};

# Tranches de tableau
my @deux_premiers = $config->{base_de_donnees}{options}->@[0, 1];

# Boucle for multi-variable (expérimental en 5.36, stable en 5.40)
use feature 'for_list';
no warnings 'experimental::for_list';
for my ($cle, $valeur) (%$config) {
    say "$cle => $valeur";
}

Entrées/Sorties Fichier

Ouverture à Trois Arguments

use v5.36;

# Bien : Ouverture à trois arguments avec autodie (module cœur, élimine 'or die')
use autodie;

sub lire_fichier($chemin) {
    open my $fh, '<:encoding(UTF-8)', $chemin;
    local $/;
    my $contenu = <$fh>;
    close $fh;
    return $contenu;
}

# Mal : Ouverture à deux arguments (risque d'injection shell, voir perl-security)
open FH, $chemin;            # NE JAMAIS faire ça
open FH, "< $chemin";        # Toujours mauvais — données utilisateur dans la chaîne de mode

Path::Tiny pour les Opérations sur Fichiers

use v5.36;
use Path::Tiny;

my $fichier = path('config', 'app.json');
my $contenu = $fichier->slurp_utf8;
$fichier->spew_utf8($nouveau_contenu);

# Parcourir un répertoire
for my $enfant (path('src')->children(qr/\.pl$/)) {
    say $enfant->basename;
}

Organisation des Modules

Structure de Projet Standard

MonApp/
├── lib/
│   └── MonApp/
│       ├── App.pm           # Module principal
│       ├── Config.pm        # Configuration
│       ├── DB.pm            # Couche base de données
│       └── Util.pm          # Utilitaires
├── bin/
│   └── monapp               # Script d'entrée
├── t/
│   ├── 00-load.t            # Tests de compilation
│   ├── unit/                # Tests unitaires
│   └── integration/         # Tests d'intégration
├── cpanfile                 # Dépendances
├── Makefile.PL              # Système de construction
└── .perlcriticrc            # Configuration de linting

Patterns d'Exportation

package MonApp::Util;
use v5.36;
use Exporter 'import';

our @EXPORT_OK   = qw(trim);
our %EXPORT_TAGS = (all => \@EXPORT_OK);

sub trim($str) { $str =~ s/^\s+|\s+$//gr }

1;

Outillage

Configuration perltidy (.perltidyrc)

-i=4        # Indentation de 4 espaces
-l=100      # Longueur de ligne de 100 caractères
-ci=4       # Indentation de continuation
-ce         # else accolé
-bar        # Accolade ouvrante sur la même ligne
-nolq       # Ne pas désindenter les chaînes longues

Configuration perlcritic (.perlcriticrc)

severity = 3
theme = core + pbp + security

[InputOutput::RequireCheckedSyscalls]
functions = :builtins
exclude_functions = say print

[Subroutines::ProhibitExplicitReturnUndef]
severity = 4

[ValuesAndExpressions::ProhibitMagicNumbers]
allowed_values = 0 1 2 -1

Gestion des Dépendances (cpanfile + carton)

cpanm App::cpanminus Carton   # Installer les outils
carton install                 # Installer les dépendances depuis cpanfile
carton exec -- perl bin/monapp # Exécuter avec les dépendances locales
# cpanfile
requires 'Moo', '>= 2.005';
requires 'Path::Tiny';
requires 'JSON::MaybeXS';
requires 'Try::Tiny';

on test => sub {
    requires 'Test2::V0';
    requires 'Test::MockModule';
};

Référence Rapide : Idiomes Perl Modernes

Pattern LegacyRemplacement Moderne
use strict; use warnings;use v5.36;
my ($x, $y) = @_;sub foo($x, $y) { ... }
@{ $ref }$ref->@*
%{ $ref }$ref->%*
open FH, "< $file"open my $fh, '<:encoding(UTF-8)', $file
hashref béniClasse Moo avec types
$1, $2, $3$+{nom} (captures nommées)
eval { }; if ($@)Try::Tiny ou try/catch natif (5.40+)
BEGIN { require Exporter; }use Exporter 'import';
Opérations manuelles sur fichiersPath::Tiny
blessed($o) && $o->isa('X')$o isa 'X' (5.32+)
builtin::true / falseuse builtin 'true', 'false'; (5.36+, expérimental)

Anti-patrons

# 1. Ouverture à deux arguments (risque de sécurité)
open FH, $nom_fichier;                     # NE JAMAIS

# 2. Syntaxe d'objet indirect (analyse ambiguë)
my $obj = new Foo(bar => 1);              # Mal
my $obj = Foo->new(bar => 1);             # Bien

# 3. Dépendance excessive à $_
map { traiter($_) } grep { valider($_) } @elements;  # Difficile à suivre
my @valides = grep { valider($_) } @elements;         # Mieux : décomposer
my @resultats = map { traiter($_) } @valides;

# 4. Désactiver strict 'refs'
no strict 'refs';                        # Presque toujours une erreur
${"Mon::Package::$var"} = $valeur;       # Utiliser un hachage à la place

# 5. Variables globales comme configuration
our $TIMEOUT = 30;                       # Mal : global mutable
use constant TIMEOUT => 30;              # Mieux : constante
# Meilleur : attribut Moo avec valeur par défaut

# 6. eval de chaîne pour le chargement de module
eval "require $module";                  # Mal : risque d'injection de code
eval "use $module";                      # Mal
use Module::Runtime 'require_module';    # Bien : chargement sécurisé
require_module($module);

Rappelez-vous : Le Perl moderne est propre, lisible et sûr. Laissez use v5.36 gérer le code standard, utilisez Moo pour les objets, et préférez les modules éprouvés du CPAN aux solutions faites maison.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills associés