API-Docker

 view release on metacpan or  search on metacpan

.claude/skills/getty-perl-moo/SKILL.md  view on Meta::CPAN

  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);
has tags   => (is => 'ro',   default  => sub { [] });    # ALWAYS coderef for refs
has id     => (is => 'lazy');                             # built on first access
sub _build_id { "id:" . $_[0]->name }

has status => (
  is      => 'rw',
  trigger => 1,                                        # calls _trigger_status on set
);
sub _trigger_status { die "bad" unless $_[1] =~ /\A(new|ok)\z/ }

has _secret => (is => 'ro', init_arg => 'secret');       # constructor param alias
```

`trigger` fires on `new()` and `set`, NOT on `default`/`builder`. Old value is NOT passed (unlike Moose). `is => 'lazy'` = lazy reader, runs builder on first access.

---

## Pattern 9 – Lifecycle Hooks

```perl
around BUILDARGS => sub {
  my ($orig, $class, @args) = @_;
  return { source => $args[0] } if @args == 1 && !ref $args[0];  # normalize
  $class->$orig(@args);
};

sub FOREIGNBUILDARGS {          # maps args to non-Moo parent's constructor
  my ($class, $args) = @_;
  return ($args->{source});
}

sub BUILD {                     # runs AFTER all attributes are set; parent→child order
  my ($self, $args) = @_;
  die "invalid" unless length $args->{source};
}
# DEMOLISH: child→parent order. Never override DESTROY directly.
```

Do NOT call `SUPER::BUILD` manually – Moo handles the chain.

---

## Pattern 10 – Strict Constructor

```perl
package StrictThing;
use Moo;
use MooX::StrictConstructor;
has size => (is => 'rw');

StrictThing->new(size => 5, colour => 'blue');  # dies: unknown attribute 'colour'
```

**Caveat:** Interacts with `namespace::clean` (can sweep `new`). If needed, protect via `-except => ['new']` or adjust import order per the module docs.

---

## Pattern 11 – Role Conflict Resolution

```perl
# Single with → conflict → dies:
# with 'RoleA', 'RoleB';   # both define foo() → fatal

# Sequential with → first wins:
with 'RoleA';   # foo() from RoleA is now in the class
with 'RoleB';   # foo() already exists → RoleA wins silently
```

"Class wins": if the class defines `foo()` itself, neither role's version is used. For complex conflict strategies: refactor roles to avoid the overlap.

---



( run in 1.083 second using v1.01-cache-2.11-cpan-d01c6094234 )