API-Docker
view release on metacpan or search on metacpan
.claude/skills/getty-perl-core/SKILL.md view on Meta::CPAN
---
name: getty-perl-core
description: "Load on any Perl edit in a Getty project â module loading, attributes, errors, strings, control flow, cpanfile, Changes, and the house choices that differ from Perl defaults."
---
# Perl Core â Getty House Rules
These rules override defaults. They are non-negotiable in Getty projects.
Object-system specifics live in `getty-perl-moo` / `getty-perl-moose`.
## Module loading
- **`use Module;` at the top.** Always. Every dependency loads at compile time.
- **`require` is forbidden as a "lazy optimization".** Never use it to shave startup. `require Foo;` inside a method body â hoist it to a top-level `use`.
- **`require` only for true runtime plugin loading** â the class comes from config/DB at runtime (`Module::Runtime::use_module($class_from_db)`). Known at write-time â `use` it.
- **`require` + `->new` in a controller action** is a red flag. Hoist to `use`.
## strict and warnings
Both are active in **every** file, without exception. How they get there differs:
- **Scripts, tests, plain modules:** `use strict; use warnings;` explicitly.
- **Classes:** `Moo`, `Moose`, `Catalyst` and anything derived from them enable both on import â do not repeat them there.
The rule is "always on", never "leave them out". Omitting them from a class is correct only because the object system already did it.
## Object system
- **One object system per distribution.** Pick Moo or Moose and use it everywhere; mixing is for boundaries a framework forces (e.g. RapidApp), not a choice.
- **`is => 'ro'` is the default.** `rw` is the exception and needs a reason.
- **`lazy_build => 1` + `sub _build_foo`** over `default => sub { ... }` for anything non-trivial.
- **`weak_ref => 1`** on attributes holding a reference back to a parent/owner â standard for nested object graphs, prevents circular refs.
- **`namespace::autoclean`** on every class file. Classes extending DBIx::Class (`MooseX::NonMoose`) use **`MooseX::MarkAsMethods autoclean => 1`** instead.
- **`no Moose;` + `__PACKAGE__->meta->make_immutable;`** at the bottom of every Moose class.
- **Types:** the tendency is to type what arrives from outside â Moose's own constraints where Moose is already there, `Types::Standard` where it is not. Not every distribution needs a type system, and none needs one for every field: `getty-perl-ty...
## Singletons
- **`->instance`** for `MooseX::Singleton` / `MooX::Singleton` classes. Never `->new` on a singleton.
- **`->new`** for everything else.
## Subroutines
- **`my ( $self, $x ) = @_;`** as the first line â explicit destructure, spaces inside the parens. Never `my $self = shift;` as argument unpacking.
- **One-liners skip unpacking** and use `shift->` or `$_[0]->`: `sub trace { shift->_logger->trace(@_) }`. This is the one place `shift` is right.
- **A builder that ignores `$self` fits on one line:** `sub _build_readonly { 0 }`.
- **`_` prefix marks private** subs and attributes. Builders for private attributes double up: `sub _build__mp`.
### Methods, not bare subs
- **In a class, every helper is a method on `$self`** â not `sub _foo {...}` invoked as `_foo($self->config, $x)`.
- **Per-process caches go on the singleton as an attribute** (`has _cache => ( is => 'ro', default => sub { {} } )`), not a `my %CACHE` package variable.
- **No package-level state** unless it is a true constant (an `%ENGINE_CLASS` lookup table counts; a per-call cache does not).
- Bare subs are fine in **non-class utility modules** imported as functions. Once a file says `use Moose`/`use Moo`, every `sub` is a method.
Why: bare subs hide what the call needs, can't be overridden or mocked, and force every caller to thread state by hand.
## Errors
- **`croak`, never `die`.** Errors report the caller's line, not ours.
- **Import it:** `use Carp qw( croak );` and call `croak(...)` bare.
- **Name the origin in the message:** `croak __PACKAGE__."->state too many args"` â or whatever identifies the operation in that module's DSL.
## Strings
- **Concatenate, do not interpolate:** `'Adding '.$f.' with '.$length.' bytes'`. Interpolate only where concatenation would be unreadable.
- **Single quotes by default.** `'...'` and `"..."` are genuinely different in Perl â `"` interpolates and processes escapes, `'` does not. Reach for `"` when you need that, not by habit.
- **Import lists as `qw( croak confess )`** â spaces inside the parens. Never rely on default exports.
## Control flow
- **Postfix `if`/`unless`** for guards and short conditions: `croak(...) if $self->readonly;`
- **`unless $x`** instead of `if !$x`.
- **Guard clauses return bare:** `return unless $res->is_success;` â not `return undef;`.
- **Nested ternaries** for a return that picks between expressions, instead of an if/elsif chain.
## Data
- **`{ %hash }` and `\%hash` are different operations, not two styles.** `{ %h }` builds a new anonymous copy; `\%h` references the existing hash. Return a copy when the caller must not mutate your state; return the reference when sharing is the poin...
- **`Path::Tiny`** for every file operation â not `File::Spec`, not bare `open`. `path(...)->child(...)->slurp_utf8`.
- **`JSON::MaybeXS`** always â never `JSON::PP`, `JSON::XS`, `Cpanel::JSON::XS` directly. Encoders get `canonical => 1, convert_blessed => 1`.
- **Every serialiser is deterministic.** MessagePack `->canonical`, DBIC `serializer_options => { canonical => 1 }`. Same rule, every format.
- **Booleans: `JSON->true` / `JSON->false`.** `use JSON::MaybeXS;` covers codec and booleans.
- `$YAML::XS::Boolean = 'JSON::PP'` is one of YAML::XS's fixed mode names, not a module choice â leave it alone.
- **Align `=>` in multi-line hash literals** when keys are of similar length.
- **Optional pairs inline:** `$cond ? ( experimental => 1 ) : (),`
## Configuration
( run in 1.956 second using v1.01-cache-2.11-cpan-2e0ccfb7a10 )