NAME Sub::Spec - Subroutine metadata & wrapping framework VERSION version 1.0.1 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::URI::http 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. 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::, e.g. Sub::Spec::Clause::args, etc. * 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. * summary => STR A one-line summary. It should be plain text without any markup. * description => STR A longer description. It should be text in Org format. See http://orgmode.org/ or Org::Parser for more details on the Org format. 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::BashComplete A helper module for Sub::Spec::CmdLine, to provide bash completion for programs using spec'ed functions. * 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::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::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::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::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::URI::http provides some convenience and options. * Sub::Spec::Object Provide object-oriented interface for Sub::Spec-related entities, like: sub response, spec, sub, request, etc. Not fully implemented. * Sub::Spec::Response::* These modules are related to (post-)processing or other stuffs to sub response. * 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::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::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::URI::* Let us refer to local or remote (HTTP) module/subroutine/spec/subroutine call using a URI string. For example, Sub::Spec::URI::pm to refer to local subs, Sub::Spec::URI::http to refer to remote subs over HTTP. * Sub::Spec::Use Import module like 'use'/'require', with module specified using URI. This has the advantage of being able to import remote subroutines (a proxy function will be created for each remote function). * 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. 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.