NAME
    Sub::Spec - Subroutine metadata specification

VERSION
    version 1.0.7

SPEFICATION VERSION
     [1, 0]

ABSTRACT
    NOTICE: The Sub::Spec specification is deprecated as of Jan 2012. Rinci
    is the new specification to replace Sub::Spec, it is about 95%
    compatible with Sub::Spec, but corrects a few issues and is more
    generic. Perinci is the Perl implementation for Rinci and many of its
    modules can handle existing Sub::Spec sub specs.

    This document specifies Sub::Spec metadata for subroutines (and
    methods). Sub::Spec is a language-neutral specification to describe
    various aspects of a subroutine. The goal is to make subroutine, which
    is the unit of reuse in many programming languages, more reusable and
    powerful.

SPECIFICATION
  Sub spec
    A sub spec (short for specification) is a hash containing certain keys,
    the keys being called clauses. There are a set of known clauses
    specified. A spec is valid if it contains only known clauses and each
    clause is valid.

    Normally a spec is created for each subroutine and placed alongside the
    code. For example, in Perl the spec is normally put in the %SPEC package
    variable:

     package MyPackage;
     our %SPEC;

     $SPEC{func1} = { ... }; # specification for func1()
     sub func1 {
         ...
     }

     $SPEC{func2} = { ... }; # specification for func2()
     sub func2 {
         ...
     }

    and so on.

  What can spec do for sub?
    In Perl, there is a growing list of Perl modules (in the Sub::Spec::*
    namespace) which can utilize the information in the spec. For example:

    *   Sub::Spec::Wrapper: wraps subroutines and implement many of the
        clauses (e.g. validate arguments specified in "args" clause,
        implement "timeout" clause using alarm() and eval block, etc).

    *   Sub::Spec::Exporter: can be used to replace Exporter or
        Sub::Exporter, automatically wrap subroutines when exporting.

    *   Sub::Spec::To::* modules: can generate documentation of various
        formats from spec, like a text usage (which one would normally see
        when giving --help argument to a command-line program).

    *   Sub::Spec::CmdLine: run subroutines from the command line. Supply
        sub arguments from command line options.

    *   Sub::Spec::HTTP::Server: serves subroutine call requests over HTTP.

    Hopefully there will be implementations to in other languages.

  Sub
    Any Perl subroutine can be given sub spec, but it needs to accept hash
    (named) arguments and return a sub response (explained below), unless
    "args_as" in spec is 'array' and "result_naked" is set to 1.

     sub foo {
         my %args = @_; # accept hash/named arguments
         ...
         return [200, "OK", $data]; # return sub response instead of just data
     }

  Sub response
    A sub response is a 2+-element arrayref:

     [STATUS, MESSAGE, DATA, METADATA]

    STATUS is an integer number between 200-599, analogous to HTTP response
    code (see "Response status" for more details on the codes). MESSAGE is a
    string (or object) containing error message. DATA contains the result,
    and is optional. METADATA is a hashref containing metadata (analogous to
    HTTP response headers), is optional and seldom used.

    This way, a subroutine can conveniently return error/status message as
    well as data. Also, since the codes are the same as in HTTP, converting
    to HTTP messages is straightforward.

  Response status
    *   1xx code

        Currently not used.

    *   2xx code

        200 should be used to mean success.

        206 can be used to signal partial content, for example: a
        read_file() sub which accepts 'byte_start' and 'byte_end' arguments
        should return 206 when only partial file content is returned. But in
        general, use 200 as some clients will simply check for this exact
        code (instead of checking for range 200-299).

    *   3xx code

        301 (moved) can be used to redirect clients to alternate location,
        though I haven't used it yet.

        304 (not modified, nothing done). Used for example by setup
        subroutines to indicate that nothing is being modified (see Setup::*
        modules in CPAN).

    *   4xx code

        400 (bad request, bad arguments) should be returned when the sub
        encountered invalid input (arguments). The wrapper code will return
        this code when arguments fail schema validation.

        403 (forbidden, access denied).

        404 not found. Can be used for example: a get_user() sub which
        accepts username/userid argument and when the user is not found.

        Note: In general, a delete_object() sub should return 200 (or
        perhaps 304, but 200 is preferred) instead of 404 when the object
        specified by the user is not found, since the goal of the delete
        function is reached anyway.

        408 (request timeout)

        409 (conflict) can be used, for example: a create_user() function
        when receiving an already existing username.

        412 (precondition failed).

    *   5xx code

        500 is the general code to use when a failure occurs during the
        execution of sub. For example, when a delete_file() sub fails to
        delete specified file (though it can return 403 which is more
        specific).

        501 (not implemented)

        503 (service unavailable)

        507 (insufficient storage)

        53x (bad spec) is used when there is something wrong with the spec.

  Clauses
    A clause must be of lowercase letters, numbers, or underscores. The next
    sections describe known clauses. A specific application/project can add
    to this list, but in general it is advisable to stick with the standard.

  Clause: type => STR (default 'sub')
    Type of subroutine. Either 'sub' for normal subroutine, 'method' for
    object method (class instance method), or 'class_method' for class
    method.

  Clause: scope => STR (default 'public')
    Subroutine scope. Either 'public' or 'private'.

  Clause: name => STR
    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.

  Clause: summary => STR
    A one-line summary. It should be plain text without any markup. It is
    like the "summary" Data::Sah clause.

  Clause: description => STR
    A longer description. It should be text in Org format. See
    http://orgmode.org/ for details on the Org format. It is like the
    "description" Data::Sah clause.

  Clause: timeout => NUM
    A number (in seconds), specifying execution time limit.

  Clause: args => HASHREF
    Args is a hash of argument names and schemas. Argument must only contain
    letters, numbers, and underscores. Schemas are Data::Sah schema.
    Example:

     args => {
         a => 'str*',
         b => ['int*' => {
             summary => 'The second argument',
             description => '... a longer description ...',
             min => 0,
             max => 100,
             ... # other schema clauses for 'int'
         }],
     }

    Sub::Spec adds a few schema clauses:

    *   arg_pos => INT, 0+

        Specify the order of argument when specified in a positional order.
        Example:

         $SPEC{multiply2} = {
             summary => 'Multiple two numbers (a & b)',
             args    => {
                 a      => ['num*' => {arg_pos=>0}],
                 b      => ['num*' => {arg_pos=>1}],
                 digits => 'int',
             },
         }

        This allows calling using named arguments as well as positional:

         multiply2(a=>4, b=>3);
         multiply2(4, 3); # after being wrapped

         # In command line:
         % cmd --a 2 --b 3
         % cmd 2 --b 3
         % cmd 2 3

    *   arg_greedy => BOOL

        Specify whether, in positional arguments, this argument should
        gobble up the rest of the arguments into array.

        Example:

         $SPEC{multiply} = {
             summary => 'Multiple numbers',
             args    => {
                 nums   => ['num*[]*' => {arg_pos=>0, arg_greedy=>1, min_len=>1}],
             },
         }
         sub multiply {
             my %args = @_;
             my $nums = $args{nums};

             my $ans = 1;
             $ans *= $_ for @$nums;
             [200, "OK", $ans];
         }

        In positional mode it can then be called:

         multiply(2, 3, 4);

        which is the same as (in normal named-argument style):

         multiply(nums => [2, 3, 4]);

        In command-line:

         % cmd 2 3 4

        instead of:

         % cmd --nums '[2, 3 ,4]'

    *   arg_complete => CODEREF

        Specifies how to complete argument value. CODEREF will be given
        arguments: word=>..., args=>.... and should return an arrayref
        containing a list of possible candidates. For an example of
        implementation for this, see Sub::Spec::BashComplete to provide tab
        completion for argument value. Example:

         $SPEC{delete_user} = {
            args => {
                username => ['str*' => {
                    arg_complete => sub {
                        my %args = @_;
                        my $word = $args{word} // "";

                        # find users beginning with $word
                        local $CWD = "/home";
                        return [grep {-d && $_ ~~ /^\Q$word/} <*>];
                    },
                }],
            },
         };

  Clause: required_args => LIST
    List of argument names that are required.

  Clause: args_as => STR (default 'hash')
    Specify what kind of arguments sub accepts. The default is 'hash', which
    means arguments will be passed as %args into Perl subroutines. The other
    options are 'hashref', 'array', 'arrayref', 'object'.

    Setting args_as to 'array' is useful if you have a "legacy" sub which
    accepts arguments directly from @_, like most normal Perl subroutines.

     $SPEC{is_palindrome} = {
         summary => 'Check whether a string is a palindrome',
         args    => {str => ['str*' => {arg_pos=>0},
                     ci  => [bool   => {arg_pos=>1, default=>0}]},
         args_as => 'array',
         result  => 'bool*',
     };
     sub is_palindrome {
         my ($str, $ci) = @_;
         $str = lc($str) if $ci;
         [200, "OK", $str eq reverse($str) ? 1:0];
     }

    Setting args_as to 'object' is useful if you want to catch typo mistakes
    in your subs:

     package My::Palindrome;
     $SPEC{is_palindrome} = {
         summary => 'Check whether a string is a palindrome',
         args    => {str => 'str*', ci=>[bool=>{default=>0}]},
         args_as => 'object',
         result  => 'bool*',
     };
     sub is_palindrome {
         my $args = shift;
         my $str  = $args->str;
         $str = lc($str) if $args->ci;
         [200, "OK", $str eq reverse($str) ? 1:0];
     }

    How this works: The wrapper (e.g. "Sub::Spec::Wrapper) will create a
    class for this argument, with accessors. Special arguments are accessed
    via _NAME (like $args-"_undo_action, $args->_reverse, etc) so make sure
    you do not define arguments with those names if you want to use object
    argument passing.

  Clause: result => SCHEMA
    The 'result' clause specifies sub's result value. The value of the
    clause is a Sah schema.

    Note that since 'result_naked' by default is false, instead of just
    returning:

     RESULT

    you'll normally have to return a proper response sub:

     [STATUS, MESSAGE, RESULT]

    Example:

     # result is an integer
     result => 'int*'

     # result is an integer starting from zero
     result => ['int*' => {ge=>0}]

     # result is an array of records
     result => ['array*' => {
                 summary => 'blah blah blah ...',
                 of      => ['hash*' => {allowed_keys=>[qw/name age address/]} ]
               }]

  Clause: result_naked => BOOL (default 0)
    Setting 'result_naked' to 1 is useful if you have a "legacy" sub which
    does not return sub response. Example:

     $SPEC{is_palindrome} = {
         summary => 'Check whether a string is a palindrome',
         args    => {str => 'str*'},
         result  => 'bool*',
     };
     sub is_palindrome {
         my %args = @_;
         my $str  = $args{str};
         [200, "OK", $str eq reverse($str) ? 1:0];
     }

    versus:

     $SPEC{is_palindrome} = {
         summary      => 'Check whether a string is a palindrome',
         args         => {str => 'str*'},
         result       => 'bool*',
         result_naked => 1,
     };
     sub is_palindrome {
         my %args = @_;
         my $str  = $args{str};
         $str eq reverse($str);
     }

  Clause: retry => INT
    Specify automatic retry upon failure. Not yet specified.

  Clause: features => HASHREF
    The "features" clause allows subroutines to express their features. Each
    hash key contains feature name, which must only contain
    letters/numbers/underscores.

    Below is the list of defined features. New feature clause may be defined
    by extension.

    *   reverse => BOOL (default 0)

        If set to true, specifies that subroutine supports reverse
        operation. To reverse, caller can add special argument '-reverse'.
        For example:

         $SPEC{triple} = {args=>{num=>'num*'}, features=>{reverse=>1}};
         sub triple {
             my %args = @_;
             my $num  = $args{num};
             [200, "OK", $args{-reverse} ? $num/3 : $num*3];
         }

         triple(num=>12);              # => 36
         triple(num=>12, -reverse=>1); # =>  4

        PLANNED: Conditional reversibility.

    *   undo => BOOL

        If set to true, specifies that subroutine supports undo operation.
        Undo is similar to 'reverse' but needs some state to be saved and
        restored for do/undo operation, while reverse can work solely from
        the arguments.

        Caller must provide one or more special arguments: -undo_action,
        -undo_hint, -undo_data when dealing with do/undo stuffs.

        To perform normal operation, caller must set -undo_action to 'do'
        and optionally pass -undo_hint for hints on how to save undo data.
        You should consult each function's documentation as undo hint
        depends on each function (e.g. if undo_data is to be saved on a
        file, -undo_hint can contain filename or base directory). Function
        must save undo data, perform action, and return result along with
        saved undo data in the response metadata (4th argument of response),
        example:

         return [200, "OK", $result, {undo_data=>$undo_data}];

        Undo data should contain information (or reference to information)
        to restore to previous state later. This information should be
        persistent (e.g. in a file/database) when necessary. For example, if
        undo data is saved in a file, undo_data can contain the filename. If
        undo data is saved in a memory structure, undo_data can refer to
        this memory structure, and so on. Undo data should be serializable.
        Caller should store this undo data in the undo stack (note: undo
        stack management is the caller's task).

        If -undo_action is false/undef, sub must assume caller want to
        perform action but without saving undo data.

        To perform an undo, caller must set -undo_action to 'undo' and pass
        back the undo data in -undo_data. Sub must restore previous state
        using undo data (or return 412 if undo data is invalid/unusable).
        After a successful undo, sub must return 200. Sub should also return
        undo_data, to undo the undo (effectively, redo):

         return [200, "OK", undef, {undo_data=>...}];

        Example (in this example, undo data is only stored in memory):

         use Cwd qw(abs_path);
         use File::Slurp;

         $SPEC{lc_file} = {
             summary  => 'Convert the *content* of file into all-lowercase',
             args     => {path=>'str*'},
             features => {undo=>1},
         };
         sub lc_file {
             my %args        = @_;
             my $path        = $args{path};
             my $undo_action = $args{-undo_action} // '';
             my $undo_data   = $args{-undo_data};

             $path = abs_path($path)
                 or return [500, "Can't get file absolute path"];

             if ($undo_action eq 'undo') {
                 write_file $path, $undo_data->{content}; # restore original content
                 utime undef, $undo_data->{mtime}, $path; # as well as original mtime
                 return [200, "OK"];
             } else {
                 my @st = stat($path)
                     or return [500, "Can't stat file"];
                 my $content = read_file($path);
                 my $undo_data = {mtime=>$st[9], content=>$content};
                 write_file $path, lc($content);
                 return [200, "OK", undef, {undo_data=>$undo_data}];
             }
         }

        To perform action, caller calls lc_file() and store the undo data:

         my $res = lc_file(path=>"/foo/bar", -undo_action=>"do");
         die "Failed: $res->[0] - $res->[1]" unless $res->[0] == 200;
         my $undo_data = $res->[3]{undo_data};

        To perform undo:

         $res = lc_file(path=>"/foo/bar", -undo_action=>"undo", -undo_data=>$undo_data);
         die "Can't undo: $res->[0] - $res->[1]" unless $res->[0] == 200;

    *   dry_run => BOOL

        If set to 1, specifies that subroutine supports dry-run (simulation)
        mode. Example:

         use Log::Any '$log';

         $SPEC{rmre} = {
             summary  => 'Delete files in curdir matching a regex',
             args     => {re=>'str*'},
             features => {dry_run=>1}
         };
         sub rmre {
             my %args    = @_;
             my $re      = qr/$args{re}/;
             my $dry_run = $args{-dry_run};

             opendir my($dir), ".";
             while (my $f = readdir($dir)) {
                 next unless $f =~ $re;
                 $log->info("Deleting $f ...");
                 next if $dry_run;
                 unlink $f;
             }
             [200, "OK"];
         }

    *   pure => BOOL

        If set to true, specifies that subroutine is "pure" and has no "side
        effects" (these are terms from functional programming / computer
        science). Having a side effect means changing something, somewhere
        (e.g. setting the value of a global variable, modifies its
        arguments, writing some data to disk, changing system date/time,
        etc.) Specifying a function as pure means, among others:

        *   the function needs not be involved in undo operation;

        *   you can safely include it during dry run;

  Clause: deps => HASHREF
    This clause specifies subroutine's dependencies. It is a hashref of dep
    clause names and values. Some dep clauses are special: "all", "any", and
    "none".

     deps => {
         DEPCLAUSE => DEPVALUE,
         ...,
         all => [
             {DEPCLAUSE=>DEPVALUE, ...},
             ...,
         },
         any => [
             {DEPCLAUSE => DEPVALUE, ...},
             ...,
         ],
         none => [
             {DEPCLAUSE => DEPVALUE, ...},
             ....,
         ],
     }

    A dependency can be anything: another subroutine, Perl version and
    module, environment variables, program, etc. It is up to the dependency
    checker library to make use of this information.

    For the dependencies to be declared as satisfied, all of the clauses
    must be satisfied.

    Below is the list of defined dependency clauses. New dependency clause
    may be defined by an extension.

    *   sub => STR

        Require that subroutine exists. STR is the name of the subroutine
        and will be assumed to be in the 'main' package if unqualified.

        Example:

         sub => 'foo'   # == main::foo
         sub => '::foo' # == main::foo
         sub => 'Package::foo'

    *   mod => STR

        Require that module is loadable. Example:

         mod => 'Moo'

    *   env => STR

        Require that an environment variable exists and has a true value.
        Example:

         env => 'HTTPS'

    *   exec => STR

        Require that an executable exists. If STR doesn't contain path
        separator character '/' it will be searched in PATH.

         exec => 'rsync'   # any rsync found on PATH
         exec => '/bin/su' # won't accept any other su

    *   code => CODEREF

        Require that CODEREF returns a true value after called. Example:

         code => sub {$>}  # i am not being run as root

    *   all => [DEPCLAUSES, ...]

        A "meta" clause that allows several dependencies to be joined
        together in a logical-AND fashion. All dependencies must be
        satisfied. For example, to declare a dependency to several
        subroutines:

         all => [
             {sub => 'Package::foo1'},
             {sub => 'Package::foo2'},
             {sub => 'Another::Package::bar'},
         ],

    *   any => [DEPCLAUSES, ...]

        Like "all", but specify a logical-OR relationship. Any one of the
        dependencies will suffice. For example, to specify requirement to
        alternative modules:

         or => [
             {mod => 'HTTP::Daemon'},
             {mod => 'HTTP::Daemon::SSL'},
         ],

    *   none => [DEPCLAUSES, ...]

        Specify that none of the dependencies must be satisfied for this
        clause to be satisfied. Example, to specify that the subroutine not
        run under SUDO or by root:

         none => [
             {env  => 'SUDO_USER'},
             {code => sub {$> != 0} },
         ],

        Note that the above is not equivalent to below:

         none => [
             {env => 'SUDO_USER', code => sub {$> != 0} },
         ],

        which means that if none or only one of 'env'/'code' is satisfied,
        the whole dependency becomes a success (since it is negated by
        'none'). Probably not what you want.

  Special arguments
    These are arguments prefixed by - (dash) that have special meanings.

SEE ALSO
    Data::Sah

    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) 2012 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.

