NAME Sub::Spec - Subroutine metadata & wrapping framework VERSION version 0.15 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: * fast and flexible parameter checking See Sub::Spec::Clause::args and Sub::Spec::Clause::result for more details. * positional as well as named arguments calling style See the export clause -args_as in Sub::Spec::Exporter. * flexible exporting See Sub::Spec::Exporter. * easy switching between exception-based and return-code error handling See the export clause -exception in Sub::Spec::Exporter. * command-line access You can basically turn your subs into a command-line program with a single line of code, complete with argument processing, --help, pretty-printing of output, and bash tab-completion. See Sub::Spec::CmdLine for more information. * HTTP REST access Creating an API from your subs is dead easy. See Sub::Spec::HTTP::Server. * generation of API documentation (POD, etc) See Sub::Spec::To::Pod on how to generate POD for your functions and Pod::Weaver::Plugin::SubSpec on how to do this when building dist with Dist::Zilla. There is also Sub::Spec::To::Text, used by Sub::Spec::CmdLine to generate text help message (--help / usage), and other format exporters like Sub::Spec::To::HTML and Sub::Spec::To::Org. * execution time limits See Sub::Spec::Clause::timeout. * automatic retries See Sub::Spec::Clause::retry. * logging * a simple undo framework See Sub::Spec::Clause::features ('undo' feature). Also see the 'reverse' feature for even simpler mechanism, if your sub applies. * and more ... More useful and interesting things to come. 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::, 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::Server serves sub call requests via HTTP; it is a PSGI application. There are also several middleware in Sub::Spec::HTTP::Middleware::* to provide functionalities like authentication/authorization. Suitable for providing remote API access. You can call 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::Loader::* Reserved for modules that load sub spec from some sources (e.g. Perl modules require()'d, files/databases, or whatever). * Sub::Spec::OO Reserved for module that provides an OO-interface to sub spec, to query its (meta)data. * 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: * File::RsyBak, Git::Bunch, Org::Parser Uses Sub::Spec::CmdLine to easily turn the subs into command-line app. Generates POD documentation from sub specs. * Setup:: modules family, e.g. Setup::Symlink, Setup::File. Uses the undo framework and dry-run feature. Generates POD documentation from sub specs. * Array::Find, Parse::PhoneNumber::ID Generates POD documentation from sub specs. 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. Related non-Perl resources Python Decorators, http://www.python.org/dev/peps/pep-0318/ , http://wiki.python.org/moin/PythonDecorators . AUTHOR Steven Haryanto 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.