Verbs on Objects

A verb is a named MOO program attached to an Object. Most verbs implement player commands — look, take, drop, say — but verbs can also be invoked as methods from other verb code (obj.title(), this.moveto(...)). Verb authoring as a craft is covered in Your First MOO Verb and Creating MOO Verbs; this page is the reference for the Verb model and its dispatch metadata.

Identity and lifecycle

Each Verb row has an origin (the Object the verb is attached to), one or more Name aliases, source code, and optional dispatch metadata (direct_object, indirect_objects). A verb is deleted when its origin object is destroyed.

At dispatch time, the parser searches caller → inventory → location → dobj → pobj for a matching verb on each object, with last match winning. See Command Parser Reference for the full search order and verb-resolution rules.

Verb attributes

Verb.pk

The unique identifying number of this Verb.

Verb.code

The Python source code for this Verb.

Verb.repo

The Repository row identifying the dataset this verb’s source came from. Populated by load_verbs; None for verbs created in-database via @edit.

Verb.filename

Absolute filesystem path to the verb source file. @reload uses this to re-read the file. None for in-database verbs.

Verb.ref

Git ref of the verb source. Reserved for future Git-backed verb sources; not yet wired into the bootstrap loader.

Verb.owner

The Object whose permissions this verb runs with. Becomes context.caller while the verb is executing. Changes require entrust permission.

Verb.origin

The Object the verb is attached to. Becomes this inside the verb body.

Verb.direct_object

Direct object specifier. One of this, any, none, or either — controls how the parser matches the verb against a typed direct object.

Verb.indirect_objects

Indirect object specifiers — one entry per accepted preposition, each with its own this/any/none rule.

direct_object is one of this, any, none, or either. indirect_objects is a ManyToMany of (preposition, specifier) pairs. Both are populated from the verb file’s shebang at load time.

Verb names

A single Verb row may have multiple Name aliases stored as separate rows. The shebang #!moo verb @reload reload_batch creates one Verb with two Names, so any of @reload or reload_batch will match. verb_name inside the verb body holds the specific alias the caller used.

Names support a single asterisk for prefix matching:

Pattern

Matches

foo

foo only (exact match).

foo*bar

foo, foob, fooba, foobar (any prefix of foobar at least as long as foo).

foo*

Any string starting with foofoo, food, foobar.

*

Anything.

The asterisk itself is not part of the matched string.

Dispatch metadata

Argument-specifier semantics:

direct_object

Effect

none (default)

Verb fires only when the command has no direct object.

any

A direct object must be present; any string is accepted.

this

Verb fires only when the parsed direct object resolves to origin.

either

Direct object is optional. this is set correctly when one is given.

indirect_objects is a list of preposition/specifier pairs. The preposition canonical forms (with, at, in, on top of, etc.) and their synonym groups live in settings.PREPOSITIONS; see Command Parser Reference for the full table. Each specifier is none, any, or this (matching the direct_object semantics above).

For the shebang grammar that populates these fields, see Creating MOO Verbs.

Permissions

Permission

Effect

read

Read the verb’s source code and metadata

write

Modify the verb’s source, dispatch metadata, or names

execute

Invoke the verb

entrust

Change the verb’s owner

grant

Set permissions on the verb

anything

Wildcard — all of the above

These checks fire automatically:

  • Verb.save() on a new row checks write on the origin Object. On an update it checks write on the verb itself, plus entrust if the owner is changing.

  • Verb.delete() checks write on the verb.

  • Verb.__call__() checks execute on the verb when there’s an active session. passthrough() passes _bypass_execute_check=True internally so a parent verb call doesn’t pay the check twice.

add_verb() requires develop permission on the target object — that’s the gate for adding a new verb to an object you don’t own.

Verb code does not need to check permissions before invoking another verb or modifying a property; the model layer raises AccessError on failure and the task runner shows it as a clean error to the player. See How Permissions Work in Verbs.

Error handling

When a verb raises an exception:

  • UserError and subclasses (including NoSuchObjectError, NoSuchVerbError, NoSuchPropertyError, UsageError, QuotaError) — the exception’s message is shown to the player as a bold red line. Verbs should raise these rather than printing errors manually.

  • PermissionError (including AccessError) — same UX as UserError; the message is the model-layer guard’s “X is not allowed to ‘Y’ on Z” string.

  • Any other exception — regular players see "An error occurred while executing the command."; wizards see the full traceback.

Because errors propagate cleanly, parser methods like get_dobj() can be called without defensive wrapping. If the named object doesn’t exist, NoSuchObjectError bubbles up and the player sees "There is no 'X' here." automatically. Catch only when you want a different message or alternative behaviour.

See also