API-Docker
view release on metacpan or search on metacpan
.claude/skills/getty-perl-moo/SKILL.md view on Meta::CPAN
has '+prefix' => (default => sub { 'Hi' }); # override via +attr
```
**Rules:** Multiple `extends` calls REPLACE (don't add). Reference defaults always as coderefs (`sub { [] }`, never `[]`).
---
## Pattern 2 â Role with `requires`
```perl
package App::Role::UppercaseName;
use Moo::Role;
requires 'name'; # contract: consumer must have name()
sub uppercase_name { uc $_[0]->name }
package App::User;
use Moo;
extends 'App::Base';
with 'App::Role::UppercaseName'; # composed; missing 'name' â loud failure
```
**Rules:** `requires` fails at composition time, not runtime. Imports inside a role land as methods on the consumer unless cleaned up â everything loaded *before* `use Moo::Role` is auto-cleaned; everything after is composed.
---
## Pattern 3 â Thin Classes (roles only, no base)
```perl
package App::Role::HasId; use Moo::Role; has id => (is => 'ro', required => 1);
package App::Role::CanDescribe;
use Moo::Role; requires 'id';
sub describe { "id=" . $_[0]->id }
package App::Thing;
use Moo;
with 'App::Role::HasId', 'App::Role::CanDescribe';
```
Use when there's no meaningful "is-a" relationship. Prefer over deep hierarchies.
---
## Pattern 4 â House-Style Import Module
```perl
package My::Mooish;
use Import::Into;
sub import {
my $target = caller;
strict->import::into($target);
warnings->import::into($target);
Moo->import::into($target);
namespace::clean->import::into($target); # after Moo, cleans stray imports
}
package App::Thing;
use My::Mooish;
has x => (is => 'ro', default => sub { 1 });
```
**Rules:** Order matters: imports â `use Moo` â `namespace::clean`. Use `namespace::autoclean` ⥠0.16 only (older versions inflate Moo classes to Moose). Use `strictures` v2 with Moo 2.
---
## Pattern 5 â Delegation via `handles`
```perl
package App::UsesCounter;
use Moo;
has counter => (
is => 'ro',
required => 1,
handles => 'App::Role::CounterAPI', # role defines the interface (inc/reset/value)
);
```
Three forms: `handles => 'RoleName'` (interface from role), `handles => [qw(inc reset)]` (list), `handles => { add => 'push' }` (rename). Does not trigger `isa`/`coerce`/`trigger` on the delegate itself.
---
## Pattern 6 â Native-Trait Delegation via `Sub::HandlesVia`
```perl
package Kitchen;
use Moo;
use Sub::HandlesVia;
use Types::Standard qw(ArrayRef Str);
has food => (
is => 'ro',
isa => ArrayRef[Str],
handles_via => 'Array',
default => sub { [] },
handles => { add => 'push', find => 'grep' },
);
```
**Use `Sub::HandlesVia` over `MooX::HandlesVia`** â the latter documents that triggers/coercions don't fire on delegated mutations. Load `Sub::HandlesVia` *after* `use Moo`.
---
## Pattern 7 â Method Modifiers
```perl
before calc => sub { die "x<0" if $_[1] < 0 }; # validate, can't change return
around calc => sub {
my ($orig, $self, $x) = @_;
return $self->$orig($x) + 1; # can change return value
};
after calc => sub { ... }; # side-effects, logging
```
**Rules:** `before`/`after` cannot alter return value; `around` can. Always forward `@_` correctly in `around`. Multiple modifiers from multiple roles stack â order is composition-order-sensitive.
---
## Pattern 8 â Attribute Options Cheatsheet
```perl
has name => (is => 'ro', required => 1);
.claude/skills/getty-perl-moo/SKILL.md view on Meta::CPAN
---
## Pattern 13 â Moose Interop
When Moose is loaded before Moo classes are compiled, Moo auto-inflates its metaclasses. This means:
- Moose class can `extends` a Moo class
- Moo class can `with` a Moose role
```perl
BEGIN { require Moose }
package MyMooseClass;
use Moose;
extends 'MyMooClass'; # works if Moose was loaded first
```
For Moose-style syntax in Moo (`isa => 'Str'`, `lazy_build`), use `MooX::late`. Avoid `Any::Moose` â deprecated, points to Moo.
---
## Type Constraints
Moo has no built-in type system â `isa` takes a coderef, and `Type::Tiny` objects
are coderefs, so `Types::Standard` plugs straight in:
```perl
use Types::Standard qw( Str ArrayRef );
has name => ( is => 'ro', isa => Str );
has tags => ( is => 'ro', isa => ArrayRef[Str], default => sub { [] } );
```
Where to type and where not, own type libraries, parameter signatures:
**`getty-perl-typing`**.
---
## Decision Guide
| Situation | Use |
|---|---|
| Shared attributes/methods, stable "is-a" | `extends` |
| Optional/horizontal feature | `Moo::Role` + `with` |
| Same pattern, different config | `MooX::Role::Parameterized` |
| Delegate method set to sub-object | `handles` |
| Array/Hash operations on attribute | `Sub::HandlesVia` |
| Logging/validation/caching wrapper | `before`/`around`/`after` |
| Catch constructor typos | `MooX::StrictConstructor` |
| Cross-project boilerplate | `Import::Into` house-style module |
| Named types | `Type::Tiny` / `Types::Standard` |
| Multiple roles define same method | Sequential `with` or refactor |
| Legacy non-Moo parent | `FOREIGNBUILDARGS` |
| Multiple inheritance | Last resort; use `mro 'c3'` |
---
## Common Pitfalls
- `default => []` â **shared state bug**. Always `default => sub { [] }`.
- `extends 'A'; extends 'B'` â replaces, does NOT add B to A. Use `extends 'A', 'B'`.
- Imports after `use Moo::Role` are **composed into consumers** as methods.
- `namespace::autoclean` < 0.16 inflates Moo classes to Moose unexpectedly.
- `trigger` does NOT receive old value (unlike Moose).
- `Sub::HandlesVia` must be loaded *after* `use Moo`.
- `BUILD` chain is automatic; calling `SUPER::BUILD` manually breaks it.
- Never override `DESTROY`; use `DEMOLISH`.
( run in 0.593 second using v1.01-cache-2.11-cpan-54e63673c56 )