
NAME
Actionable - auto-populate Raku classes from grammar match objects
SYNOPSIS
use Actionable;
grammar Grammar {
token TOP {
<invoice-line>
[ \n+ <ws> [ <field-line> | <item-line> ] ]*
\n*
}
rule invoice-line { invoice <id> }
rule field-line { | date <date>
| client <client=quoted>
| tax <tax-rate=number> '%' }
rule item-line { item <description=quoted>
hours <hours=number>
rate <rate=number> }
token id { <[A..Za..z0..9_-]>+ }
token date { \d**4 '-' \d**2 '-' \d**2 }
token quoted { '"' <( <-["]>+ )> '"' }
token number { \d+ [ '.' \d+ ]? }
token ws { \h* } #horizontal whitespace only
}
class Item does Actionable {
has ($.description, $.hours, $.rate);
method subtotal { $.hours * $.rate }
}
class Invoice does Actionable {
has ($.id, $.date, $.client, $.tax-rate = 0);
has Item @.items;
method transform(Str $attr, $raw) {
$attr eq 'tax-rate' ?? $raw / 100 !! $raw
}
method subtotal { @.items.map(*.subtotal).sum }
method tax { $.subtotal * $.tax-rate }
method total { $.subtotal + $.tax }
}
class Actions {
method TOP($/) {
my $inv = Invoice.action($<invoice-line>);
{ $inv.action($_) } for $<field-line>;
{ $inv.items.push(Item.action($_)) } for $<item-line>;
make $inv;
}
}
DESCRIPTION
Actionable is a role that eliminates boilerplate in Raku grammar Actions classes. Mix it into any class to get an action method that auto-populates attributes from a grammar match object.
action dispatches on whether the invocant is defined:
Type object (MyClass.action($match)) — creates and returns a new instance, populating scalar attributes from named captures in $match.
Instance ($obj.action($match)) — updates the instance in place from $match and returns self.
Attribute mapping
By default each attribute is looked up by its own name as a named capture. To override, provide a capture-map method returning a Hash of attr-name =E<gt> dot-path:
method capture-map {
{ qty => 'number.0', # $match<number>[0]
price => 'number.1' } # $match<number>[1]
}
Dot-path segments are hash keys or array indices (all-digit segments are treated as indices).
Alternatively, use Raku's aliased capture syntax in the grammar to name captures after the target attribute — avoiding capture-map entirely:
rule item-line { item <description=quoted-string> hours <hours=number> rate <rate=number> }
Positional (quantified) captures
When a named capture is produced by a quantified token (*, +, ?) it resolves to a Positional. action handles this transparently:
Zero matches — the attribute is skipped (left at its default).
One match — the single element is unwrapped and used normally.
Two or more matches — action dies with an unambiguous error; use capture-map or an explicit Actions method to select the desired element.
Type coercion
Attributes typed as Numeric (or any subtype: Int, Real, Rat, Num) are coerced with +; all others with ~. Array and hash attributes are skipped automatically.
Post-coercion transforms
Override transform to adjust a value after coercion:
method transform(Str $attr, $raw) {
$attr eq 'tax-rate' ?? $raw / 100 !! $raw
}
Sub-match .made values
If a sub-match has been processed by an Actions method that called make, action prefers the .made value over raw string coercion. This enables nested-class population without any explicit wiring:
class jCard does Actionable {
has Address $.adr; # populated automatically from $<adr>.made
...
}
class Actions {
method adr($/) { make Address.action($/) } # sets .made on $<adr>
method TOP($/) { make jCard.action($/) } # picks up .made automatically
}
Injecting values via named arguments
Pass named arguments to action to supply or override attribute values directly. Named arguments win over both .made and raw captures, and are used as-is (no coercion, no transform call):
make jCard.action($/, adr => $pre-built-address);
Precedence
For each attribute, action resolves its value in this order:
- Named argument (
*%h) — highest priority
.made of the resolved capture — if the sub-match was processed by an actions method
- Raw capture coerced and passed through
transform — fallback
method action-hash
method action-hash() returns Mu
get a hash of all the attrs with values populated via .action
method action-to-json
method action-to-json() returns Mu
get json of all the attrs with values populated via .action
method raku
method raku() returns Mu
fallback method raku to preempt 'Object<9230298340589>'
method action-table
method action-table(
%h = Code.new
) returns Mu
basic table of action-hash values $k => $v
AUTHOR
librasteve librasteve@furnival.net
COPYRIGHT AND LICENSE
Copyright 2026 librasteve
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.