NAME

Sub::Spec - Subroutine metadata & wrapping framework

VERSION

version 1.0.0

SYNOPSIS

Write your subroutine and add a metadata ($SPEC{subname}):

package MyModule;

use 5.010;
use strict;
use warnings;

use Sub::Spec::Exporter ...;

our %SPEC;
$SPEC{pow} = {
    summary     => 'Exponent a number',
    description => <<'_',
...
_
    args        => {
        base => [float => {summary=>"Base number", required=>1, arg_pos=>0}],
        exp  => [float => {summary=>"Exponent"   , required=>1, arg_pos=>1}],
    },
};
sub pow {
    my (%args) = @_;
    return [200, "OK", $arg{base} ** $arg{exp}];
}

Use your sub:

package MyApp;
use 5.010;
use Sub::Spec;
use MyModule qw(pow);
my $res;

# schema checking (NOT YET IMPLEMENTED)
#$res = pow(base => 1);   # [400, "Missing argument: exp"]
#$res = pow(base => "a"); # [400, "Invalid argument: base must be a float"]

$res = pow(base => 2, exp=>10); # [200, "OK", 1024]
say $res->[2];

Use positional arguments (NOT YET IMPLEMENTED):

use MyModule pow => {args_as=>'ARRAY'};
$res = pow(2, 10); # [200, "OK", 1024]

Return data only instead of with status code + message (NOT YET IMPLEMENTED):

use MyModule pow => {result_naked=>1};

say pow(base=>2, exp=>10); # 1024
say pow(base=>2); # now throws exception due to missing required arg 'exp'

Use your subs from the command line (see Sub::Spec::CmdLine for more details):

% cat script.pl
#!/usr/bin/perl
use Sub::Spec::CmdLine qw(run);
run(module=>"MyModule", sub=>"pow");

% script.pl --help
(Usage message ...)

% script.pl --base 2 --exp 10
1024

% script.pl 2 10
1024

% script.pl 2
Error: Missing required argument exp

Create HTTP REST API from your subs (see Sub::Spec::HTTP::Server and Sub::Spec::HTTP::Client for more details):

% cat apid.psgi
#!/usr/bin/perl
use Sub::Spec::HTTP::Server;
use My::Module1;

my $app = sub {
    Sub::Spec::HTTP::Server->psgi_app();
};

% plackup apid.psgi

and then you can do:

% curl 'http://localhost:5000/MyModule/pow?base=2&exp=10'
[200,"OK",1024]

DESCRIPTION

Subroutines are an excellent unit of reuse; in some ways they are even superior to objects (simpler, map better to HTTP/network programming due to being stateless, etc). Sub::Spec aims to make your subs much more useful, reusable, powerful. All you have to do is provide some metadata (a "spec") for your subs (see Sub::Spec::Manual::Spec for a full description of the spec), and then there will be a family of modules doing useful things for you.

Below are the features provided by Sub::Spec:

SPECIFICATION VERSION

1.0

WHAT ALREADY WORKS AND WHAT HAS NOT

While the Sub::Spec specification is stabilizing, implementation-wise there are still key components that are missing. Sub::Spec::Wrapper, the main meat, has not been written, so almost all extra sub functionalities do not work yet (this include argument validation, timeouts and retries, conversion of argument style, etc). For example, right now I still check my arguments manually, but this portion of the code can be removed later when the wrapper is ready.

What already works?

  • command-line access

  • exporting sub specs to text/html/org/pod

  • dependency checking (see Sub::Spec::Runner)

  • HTTP API stuffs

See "MODULES FAMILY" for more details.

CLAUSES

Here are the general clauses of the spec. For the rest of the clauses see respective Sub::Spec::Clause::<CLAUSE_NAME>, e.g. Sub::Spec::Clause::args, etc.

  • name

    The name of the subroutine. Useful for generating help/usage information, or when aliasing subroutines (and reusing the spec) and finding out the canonical/original name of the subroutine.

  • summary

    A one-line summary. It should be plain text without any markup.

  • description

    A longer description. Currently the format of the text inside is not yet specified. It is probably going to be Markdown, not POD/HTML.

Sub::Spec is extensible, you can add your own clauses (see Sub::Spec::Manual::Spec for more information).

MODULES FAMILY

Below is the namespace organization and list of Sub::Spec modules/distributions family.

  • Sub::Spec

    The main module, contains specification, general documentation, and current best practice.

  • Sub::Spec::Schema

    This module name is reserved, it will contain the Data::Sah's schema for the sub spec, so you can validate your sub specs with it.

    Not yet implemented.

  • Sub::Spec::Wrapper

    The module that actually generates the wrapper code for subroutines (including code for validating arguments and all the other code necessary to implement the other spec clauses). Normally, it will be used through Sub::Spec::Exporter or some kind of invoker in your module.

    Not yet implemented.

  • Sub::Spec::Exporter

    The exporter to be used by modules containing spec'ed functions, instead of, say, Exporter or Sub::Exporter. It will have some advantages like the ability to parse sub specs (e.g. for export tags), generation of wrapper code, etc.

    Not yet implemented. For now I personally still use the good ol' Exporter. As a recommended best practice, do not export anything by default. Put everything worth exporting into @EXPORT_OK.

  • Sub::Spec::GetArgs::*

    These modules, normally with the help of sub spec, parse some form of input into subroutine arguments (%args). Example modules: Sub::Spec::GetArgs::Argv (from @ARGV), Sub::Spec::GetArgs::GetPost (from HTTP GET/POST request data), or Sub::Spec::GetArgs::PathInfo (from CGI/PSGI's PATH_INFO).

    Someday I also plan to write Sub::Spec::GetArgs::Console to get args from interactive console prompts.

    I also envision something like getting args from a GUI/TUI dialog.

  • Sub::Spec::To::*

    These modules convert (export) sub spec to various other outputs, example: Sub::Spec::To::Pod (e.g. the generated POD is to be inserted into Perl module files; see Pod::Weaver::Plugin::SubSpec), Sub::Spec::To::Text (e.g. for generating --help/usage message), Sub::Spec::To::HTML and Sub::Spec::To::Org (e.g. to generate API documentation).

  • Sub::Spec::Runner

    With Sub::Spec you can specify dependencies between subs (and to external objects like an OS software package, a binary, etc). This module can be used to check dependencies before running your sub, as well as running several subs in custom order.

  • Sub::Spec::CmdLine

    This module provides an easy way to execute your subs from the command line, and even provides extra support like bash completion. Internally, it is just a composition of Sub::Spec::GetArgs::Argv, Sub::Spec::Runner, Sub::Spec::BashComplete.

  • Sub::Spec::Gen:*

    These modules generate sub spec (and/or the sub code) from some other, probably higher-level or more abstract, specification. Example: Sub::Spec::Gen::ReadTable.

    Someday I also plan to write Sub::Spec::Gen::ReadTable::SQL, to generate table access functions from a SQL database table.

  • Sub::Spec::Caller

    A helper module to load wanted module and call its sub, but with some options. See its documentation for more details.

  • Sub::Spec::BashComplete

    A helper module for Sub::Spec::CmdLine, to provide bash completion for programs using spec'ed functions.

  • Sub::Spec::HTTP::*

    These modules all relate to providing remote sub call access via HTTP.

    Sub::Spec::HTTP is the specification. It provides a standard to cooperate among different languages/implementations.

    Sub::Spec::HTTP::Server serves sub call requests via HTTP; it is a PSGI application. There are also several middleware in Plack::HTTP::Middleware::* to provide functionalities like authentication/authorization/custom request parsing. Suitable for providing remote API access.

    You can use any HTTP client to use the API service built using the tool mentioned above, but Sub::Spec::HTTP::Client provides some convenience and options.

  • Sub::Spec::FromURI::*

    Let us refer to local or remote (HTTP) subroutines/specs/subroutine calls using a URI string. For example, Sub::Spec::FromURI::pm to refer to local subs, Sub::Spec::FromURI::http to refer to remote subs over HTTP.

  • Sub::Spec::?

    Reserved for modules that do some kind of transformation/modification to sub specs.

    But honestly, since sub specs are just data structures, you can use whatever tool you want to transform them.

FAQ

See Sub::Spec::Manual::FAQ

SEE ALSO

Example applications/modules

The following applications/modules, among others, are already using Sub::Spec to varying degrees:

Modules used

Data::Sah for schema checking.

Log::Any for logging.

Alternative modules

If you just want to give named arguments, you might want to consider Sub::NamedParams.

You can check out Sub::Attempts for retries.

There are a gazillion modules for parameter checking. Data::Sah lists a few of them.

Python Decorators, http://www.python.org/dev/peps/pep-0318/ , http://wiki.python.org/moin/PythonDecorators .

AUTHOR

Steven Haryanto <stevenharyanto@gmail.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Steven Haryanto.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.