Skip to content
Back to skills

Perl Mojo

ASecurity

Use when writing Mojolicious or Mojo::Base code — attributes and defaults, roles, Mojo::UserAgent, Mojo::IOLoop, the core Mojo::* helpers.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
ai-agentsbashdebuggingapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add Getty/skills --skill perl-mojo --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Perl Mojo?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Perl Mojo
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/getty-perl-mojo/badge)](https://www.skillsdirectory.com/skills/getty-perl-mojo)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: perl-mojo
description: Use when writing Mojolicious or Mojo::Base code — attributes and defaults, roles, Mojo::UserAgent, Mojo::IOLoop, the core Mojo::* helpers.
---

# Perl/Mojo – Object System & Toolkit

## Core Principle
`Mojo::Base` is a **complete object system in one import**: strict/warnings/utf8, the
current feature bundle, attributes, and inheritance. Reach for it whenever the
surrounding code already lives in Mojo-land — mixing object systems inside one
distribution costs more than it buys.

All behaviour below is verified against the installed Mojolicious, not quoted from memory.

---

## Pattern 1 – Class, subclass, role

```perl
package App::Base;
use Mojo::Base -base, -signatures;        # standalone class

package App::Child;
use Mojo::Base 'App::Base', -signatures;  # subclass: parent name replaces -base

package App::Role::Loud;
use Mojo::Base -role, -signatures;        # role (Role::Tiny under the hood)
requires 'name';
sub shout ($self) { uc $self->name }

package main;
use Mojo::Base -strict;                   # scripts: strict/warnings/utf8/features, no OO
```

**Rules:** The first argument picks the mode — `-base`, a parent class name, `-role`,
or `-strict`. Add `-signatures` for subroutine signatures, `-async_await` for
`async`/`await`.

---

## Pattern 2 – Attributes

```perl
package App::Server;
use Mojo::Base -base, -signatures;

has name    => 'PerlServer';        # static default, shared value
has port    => 8080;
has debug   => 0;                   # falsy defaults are kept as-is
has clients => sub { [] };          # coderef: fresh per instance
has ua      => sub { Mojo::UserAgent->new };
```

**Rules:** A **coderef default is evaluated lazily** — on first read of the accessor,
once per instance, then cached in the object. Anything mutable (arrayref, hashref,
object) must be a coderef, otherwise every instance shares one value. Attribute names
must match `/^[a-zA-Z_]\w*$/`.

`App::Server->attr(added => 'later')` adds an attribute at runtime, which is how
plugins extend a class they do not own.

---

## Pattern 3 – Overriding an inherited default

```perl
package MCP::Server;
use Mojo::Base -base, -signatures;
has name    => 'PerlServer';
has version => '1.0.0';

package MCP::Run::Bash;
use Mojo::Base 'MCP::Server', -signatures;
our $VERSION = '0.106';
has name    => 'mcp-run-bash';      # re-declare: overrides the parent default
has version => $VERSION;
```

**Rules:** Redeclaring `has` in the subclass is the override — it installs a fresh
accessor in the child package. Explicit constructor arguments still win over any
default, so `MCP::Run::Bash->new(name => 'other')` reports `other`. Put identity
defaults on the **class**, not in the `bin/` script that happens to launch it, so
library users inherit them too.

---

## Pattern 4 – Chainable accessors

```perl
my $server = App::Server->new;
$server->name('api')->port(9090)->debug(1);   # setters return the invocant
my $name = $server->name;                      # getter returns the value

App::Server->new(name => 'a');                 # hash
App::Server->new({name => 'a'});               # hashref — both accepted
```

**Rules:** Every setter returns `$self`, so calls chain. `->tap(sub { ... })` runs a
side effect mid-chain and returns the invocant: `$obj->tap(sub { $_[0]->name('x') })`.

---

## Pattern 5 – Composing roles

```perl
package App::Quiet;
use Mojo::Base -base, -signatures;
has name => 'quiet';

package main;
my $loud = App::Quiet->with_roles('App::Role::Loud')->new;
say $loud->shout;                              # QUIET
```

**Rules:** `with_roles` builds (and caches) a new class with the roles applied and
returns its name — compose first, construct second. A leading `+` in the role name
expands to the invocant's namespace: `App::Quiet->with_roles('+Loud')` means
`App::Quiet::Role::Loud`. Roles can also be applied to a single object with
`$obj->with_roles(...)`. `requires 'name'` in the role is checked at composition time.

---

## Pattern 6 – Lazy construction of expensive members

```perl
package App::Pipeline;
use Mojo::Base -base, -signatures;
has filters => sub { shift->_build_filters };  # built on first use, then cached
```

**Rules:** Lazy defaults defer **construction**, not **loading** — the class you
construct still has to be `use`d at compile time. Load the module with a plain `use`
at the top and keep the coderef for the expensive part; that way a missing or broken
dependency stops the process at startup rather than on a user's first request.

---

## Toolkit – the parts worth knowing

```perl
use Mojo::JSON qw(encode_json decode_json true false);
encode_json {ok => true, no => false};         # {"no":false,"ok":true}
decode_json('{"n":1}')->{n};                   # 1

use Mojo::File qw(path);
path('/tmp/a/b.txt')->basename;                # b.txt
path('/tmp/x')->spurt($data);                  # write
path('/tmp/x')->slurp;                         # read
path('/tmp')->list_tree->each(sub { ... });    # walk

use Mojo::Collection qw(c);
c(1,2,3,4)->grep(sub { $_ % 2 })->map(sub { $_ * 10 })->join('|');   # 10|30
c(3,1,2)->sort->first;                          # 1

use Mojo::Util qw(trim dumper b64_encode encode decode);
trim("  x  ");                                  # x
print dumper $ref;                              # debugging dump
```

**Rules:** `Mojo::JSON`'s `true`/`false` survive a decode/encode roundtrip as real JSON
booleans — plain `1`/`0` do not. `Mojo::Collection` methods return collections, so they
chain; `->each`/`->join`/`->first` leave the chain.

---

## Async

```perl
use Mojo::Base -strict, -signatures, -async_await;   # -signatures is its own flag
use Mojo::Promise;

async sub fetch ($url) {
  my $tx = await $ua->get_p($url);
  return $tx->result->json;
}

Mojo::Promise->all(map { fetch($_) } @urls)->then(sub (@results) { ... })->wait;
```

**Rules:** `-async_await` enables `async`/`await` only — a signatured `async sub` also
needs `-signatures` in the same import. `->wait` drives the event loop until settled and
belongs at the top level, never inside a running loop. `all` hands its callback **one
arrayref per input promise** (`[$first], [$second], ...`), preserving input order.
`Mojo::IOLoop->timer`/`->recurring` schedule work; `Mojo::IOLoop->start` runs the loop
unless one is already running.

---

## Conventions

- **POD attributes** documented as `=attr`, methods as `=method` — matching whatever the
  distribution's release bundle expects.
- **`$VERSION` before `has`.** `our $VERSION` is assigned at compile time near the top of
  the file, so `has version => $VERSION;` further down sees the value in both a checkout
  and a built distribution.
- **One object system per distribution.** If the base class is `Mojo::Base`, subclasses
  stay `Mojo::Base`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…