perlancar > Perinci-Sub-Wrapper > Perinci::Sub::Wrapper

Download:
Perinci-Sub-Wrapper-0.67.tar.gz

Dependencies

Annotate this POD

Website

CPAN RT

Open  0
View/Report Bugs
Module Version: 0.67   Source  

NAME ^

Perinci::Sub::Wrapper - A multi-purpose subroutine wrapping framework

VERSION ^

This document describes version 0.67 of Perinci::Sub::Wrapper (from Perl distribution Perinci-Sub-Wrapper), released on 2014-12-10.

SYNOPSIS ^

For dynamic usage:

 use Perinci::Sub::Wrapper qw(wrap_sub);
 my $res = wrap_sub(sub_name => "mysub", meta=>{...});
 my ($wrapped_sub, $meta) = ($res->[2]{sub}, $res->[2]{meta});
 $wrapped_sub->(); # call the wrapped function

DESCRIPTION ^

Perinci::Sub::Wrapper (PSW for short) is an extensible subroutine wrapping framework. It generates code to do stuffs before calling your subroutine, like validate arguments, convert arguments from positional/array to named/hash or vice versa, etc; as well as generate code to do stuffs after calling your subroutine, like retry calling for a number of times if subroutine returns a non-success status, check subroutine result against a schema, etc). Some other things it can do: apply a timeout, currying, and so on.

PSW differs from other function composition or decoration system like Python decorators (or its Perl equivalent Python::Decorator) in a couple of ways:

Normally you do not use PSW directly in your applications. You might want to check out Perinci::Access::Perl and Perinci::Exporter on examples of wrapping function dynamically (during runtime), or Dist::Zilla::Plugin::Rinci::Wrap on an example of embedding the generated wrapping code to source code during build.

FUNCTIONS ^

wrap_sub(%args) -> [status, msg, result, meta]

Wrap subroutine to do various things, like enforcing Rinci properties.

Arguments ('*' denotes required arguments):

Return value:

Returns an enveloped result (an array).

First element (status) is an integer containing HTTP status code (200 means OK, 4xx caller error, 5xx function error). Second element (msg) is a string containing error message, or 'OK' if status is 200. Third element (result) is optional, the actual result. Fourth element (meta) is called result metadata and is optional, a hash that contains extra information.

The wrapped subroutine along with its new metadata (hash)

Aside from wrapping the subroutine, the wrapper will also create a new metadata for the subroutine. The new metadata is a clone of the original, with some properties changed, e.g. schema in args and result normalized, some values changed according to the convert argument, some defaults set, etc.

The new metadata will also contain (or append) the wrapping log located in the x.perinci.sub.wrapper.logs attribute. The wrapping log marks that the wrapper has added some functionality (like validating arguments or result) so that future nested wrapper can choose to avoid duplicating the same functionality.

EXTENDING ^

The framework is simple and extensible. Please delve directly into the source code for now. Some notes:

The internal uses OO.

The main wrapper building mechanism is in the wrap() method.

For each Rinci property, it will call handle_NAME() wrapper handler method. The handlemeta_NAME() methods are called first, to determine order of processing. You can supply these methods either by subclassing the class or, more simply, monkeypatching the method in the Perinci::Sub::Wrapper package.

The wrapper handler method will be called with a hash argument, containing these keys: value (property value), new (this key will exist if convert argument of wrap() exists, to convert a property to a new value).

For properties that have name in the form of NAME1.NAME2.NAME3 (i.e., dotted) only the first part of the name will be used (i.e., handle_NAME1()).

VARIABLES ^

$Log_Wrapper_Code (BOOL)

Whether to log wrapper result. Default is from environment variable LOG_PERINCI_WRAPPER_CODE, or false. Logging is done with Log::Any at trace level.

METHODS ^

The OO interface is only used internally or when you want to extend the wrapper.

ENVIRONMENT ^

LOG_PERINCI_WRAPPER_CODE (bool)

If set to 1, will log the generated wrapper code. This value is used to set $Log_Wrapper_Code if it is not already set.

RINCI FUNCTION METADATA ^

x.perinci.sub.wrapper.disable_validate_args => bool

Can be set to 1 to set validate_args to 0 by default. This is used e.g. if you already embed/insert code to validate arguments by other means and do not want to repeat validating arguments. E.g. used if you use Dist::Zilla::Plugin::Rinci::Validate.

x.perinci.sub.wrapper.disable_validate_result => bool

Can be set to 1 to set validate_result to 0 by default. This is used e.g. if you already embed/insert code to validate result by other means and do not want to repeat validating result. E.g. used if you use Dist::Zilla::Plugin::Rinci::Validate.

x.perinci.sub.wrapper.logs => array

Generated/added by this module to the function metadata for every wrapping done. Used to avoid adding repeated code, e.g. to validate result or arguments.

PERFORMANCE NOTES ^

The following numbers are produced on an Intel Core i5-2400 3.1GHz desktop using PSW v0.51 and Perl v5.18.2. Operating system is Debian sid (64bit).

For perspective, empty subroutine (sub {}) as well as sub { [200, "OK"] } can be called around 5.3 mil/sec.

Wrapping this subroutine sub { [200, "OK"] } and this simple metadata {v=>1.1} using default options yields call performance for $sub->() of about 0.9 mil/sec. With validate_args=>0 and validate_result=>0, it's 1.5 mil/sec.

As more (and more complex) arguments are introduced and validated, overhead will increase. The significant portion of the overhead is in argument validation. For example, this metadata {v=>1.1, args=>{a=>{schema=>"int"}}} yields 0.5 mil/sec.

FAQ ^

General

Debugging

caller() doesn't work from inside my wrapped code!

Wrapping adds at least one or two levels of calls: one for the wrapper subroutine itself, the other is for the eval trap when necessary.

This poses a problem if you need to call caller() from within your wrapped code; it will also be off by at least one or two.

The solution is for your function to use the caller() replacement, provided by Perinci::Sub::Util. Or use embedded mode, where the wrapper code won't add extra subroutine calls.

SEE ALSO ^

Perinci, Rinci

Python::Decorator

Dist::Zilla::Plugin::Rinci::Wrap

Dist::Zilla::Plugin::Rinci::Validate

HOMEPAGE ^

Please visit the project's homepage at https://metacpan.org/release/Perinci-Sub-Wrapper.

SOURCE ^

Source repository is at https://github.com/perlancar/perl-Perinci-Sub-Wrapper.

BUGS ^

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Wrapper

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHOR ^

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE ^

This software is copyright (c) 2014 by perlancar@cpan.org.

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

syntax highlighting: