Dispatch::Fu

Dispatch::Fu is a small, production-worthy Perl module intended for production use. It turns arbitrary input into a static dispatch key and then uses that key to select a handler from a hash-based dispatch table.

It is intended for situations where ordinary hash dispatch is attractive, but the value that determines the action is not already a convenient one-to-one hash key. The dispatch block performs whatever classification or reduction your application needs; the resulting static key is matched to a handler registered with on.

The implementation is deliberately lightweight. Runtime dependencies are limited to Perl core modules, and the public interface stays focused on five routines:

The distribution includes regression tests for repeated dispatch, case introspection, default handling, diagnostics, return-value behavior, reference unpacking, invalid handlers, and false-but-valid dispatch keys.

Example

use strict;
use warnings;
use Dispatch::Fu;    # exports dispatch, on, cases, xdefault, xshift_and_deref

my $input = [qw/1 2 3 4 5/];

my $result = dispatch {
    my $values = shift;

    return scalar(@$values) > 5
      ? q{bucket5}
      : sprintf q{bucket%d}, scalar @$values;
}
$input,
  on bucket0 => sub { return q{bucket 0} },
  on bucket1 => sub { return q{bucket 1} },
  on bucket2 => sub { return q{bucket 2} },
  on bucket3 => sub { return q{bucket 3} },
  on bucket4 => sub { return q{bucket 4} },
  on bucket5 => sub { return q{bucket 5} };

print "$result\n";    # bucket 5

The dispatch block is ordinary Perl. It can classify a range, inspect multiple values stored in a reference, normalize external input, apply application rules, or perform any other deterministic reduction that produces one of the registered keys.

Why use Dispatch::Fu?

A conventional Perl dispatch table is concise and fast when the decision is already represented by a static string:

my $handlers = {
    start => sub { ... },
    stop  => sub { ... },
};

$handlers->{$action}->();

Dispatch::Fu keeps that simple final lookup while adding an explicit stage for computing the key. This makes it useful for cases traditionally expressed as long if/elsif chains, switch/case constructs, given/when, smartmatch logic, or other ad hoc classification code.

my $result = dispatch {
    my $value = shift;

    return q{small}  if $value < 10;
    return q{medium} if $value < 100;
    return q{large};
}
$value,
  on small  => sub { ... },
  on medium => sub { ... },
  on large  => sub { ... };

Defaults and introspection

xdefault is a shortcut for the common case where an input value should be used directly when it exactly names a registered case. Matching is literal, not substring or regular-expression matching, and false-but-defined keys such as "0" are valid:

my $result = dispatch {
    xdefault shift;
}
$action,
  on default => sub { ... },
  on start   => sub { ... },
  on stop    => sub { ... };

cases returns the currently registered case names in sorted order. During the classification block it includes the cases registered with on plus the built-in default case. The table is reset before the selected handler runs, so outside the classification block cases reflects only the built-in default case. This keeps dispatch calls isolated from one another.

CGI::Tiny example

A small CGI application is a natural fit when the action depends on more than one request value. Here the dispatch key is derived from both the HTTP method and request path, while the selected handler receives the original CGI::Tiny object:

use strict;
use warnings;
use CGI::Tiny;
use Dispatch::Fu;

cgi {
    my $cgi = $_;

    dispatch {
        my $cgi = shift;
        my $method = $cgi->method;
        my $path   = $cgi->path;

        return q{home}
          if $method eq q{GET} and $path eq q{/};

        return q{create_item}
          if $method eq q{POST} and $path eq q{/item};

        return q{not_found};
    }
    $cgi,
      on home => sub {
          my $cgi = shift;
          return $cgi->render(html => q{<h1>Home</h1>});
      },
      on create_item => sub {
          my $cgi = shift;
          my $name = $cgi->param(q{name});
          return $cgi->render(text => qq{created: $name});
      },
      on not_found => sub {
          my $cgi = shift;
          $cgi->set_response_status(404);
          return $cgi->render(text => q{Not Found});
      };
};

This is useful for modest CGI programs with a small static set of actions; it is not intended to replace a full routing framework.

Reference unpacking

dispatch passes one scalar value to the classification block and selected handler. When that scalar is a reference, xshift_and_deref can remove common unpacking boilerplate:

my ($x, $y, $z) = xshift_and_deref @_;

It supports hash, array, and scalar references.

Diagnostics

Dispatch::Fu detects common dispatch mistakes and reports them clearly. It croaks when no cases are supplied, croaks when the computed key does not map to a CODE handler, and warns when on is accidentally used in void or scalar context (a common sign that a semicolon was used where a comma was intended).

Stability

Dispatch::Fu is maintained as production code rather than a proof of concept. The module intentionally keeps a small API and implementation surface, and changes should preserve existing calling conventions and lightweight runtime requirements. See Changes for release history and t/ for executable usage examples and regression coverage.

License

Same terms as Perl itself.