Rinci-1.1.43/0000755000175000017500000000000012256561363010311 5ustar u1u1Rinci-1.1.43/Changes0000644000175000017500000003155312256561363011613 0ustar u1u1Revision history for Rinci 1.1.43 2013-12-25 (SHARYANTO) - Ho ho ho! - function: Add argument spec's 'cmdline_on_getopt' and 'cmdline_on_getarg'. 1.1.42 2013-11-14 (SHARYANTO) - function: Add argument spec's 'delete' and 'element_completion' properties. 1.1.41 2013-11-08 (SHARYANTO) - function: Introduce value 'file' for argument spec property 'cmdline_src', to allow function to get file content in its argument. - function: Introduce property 'test' on example spec, to allow test module to skip testing a certain examples. 1.1.40 2013-10-28 (SHARYANTO) - result: Add properties 'func.*'. 1.1.39 2013-10-15 (SHARYANTO) - with the smell of burning goat meat in the house! - function: examples: Introduce 'src' and 'src_plang' so that more general examples can be specified. - function: cmdline_aliases: 'code' now gets (\%args, $val) instead of just (\%args). 1.1.38 2013-09-15 (SHARYANTO) - result: Add property 'logs'. 1.1.37 2013-09-13 (SHARYANTO) - result: Replace property 'error_stack' with 'prev'. 1.1.36 2013-09-07 (SHARYANTO) - Rename property 'entity_version' to 'entity_v' to be more consistent with DefHash ('defhash_v'). 1.1.35 2013-04-11 (SHARYANTO) - Introduce property 'x' (from DefHash 1.0.3). 1.1.34 2012-11-07 (SHARYANTO) - function: Introduce status 44x and 54x (experimental). 1.1.33 2012-11-07 (SHARYANTO) - No spec changes. (Temporarily?) split Rinci::Schema to its own dist so I can release them often separately. 1.1.32 2012-11-01 (SHARYANTO) - result: Add property 'error_stack'. - function: property 'result': Add key 'statuses'. 1.1.31 2012-09-19 (SHARYANTO) - Base specification on DefHash. - Remove 'text_markup' property. Will depend on DefHash for this. 1.1.30 2012-09-07 (SHARYANTO) - No spec changes. Rename back a file (Rinci/Schema.pm) that I thought was not used (me--). [CT] 1.1.29 2012-09-04 (SHARYANTO) - Transaction: In 'check_state' phase, specify that result message should contain a description of what needs to be fixed, or how it is already fixed, or how it is unfixable; the message can then be logged by TM to be displayed to user. 1.1.28 2012-08-29 (SHARYANTO) [INCOMPATIBLE CHANGE] - Transaction: To link between 'check_state' call and 'fix_state' call (e.g. function want to preserve some value between call), previously in 'fix_state' call TM passes '-tx_undo_actions' containing the undo actions from the 'check_state'. Now a simpler way is introduced to replace this: '-tx_action_id' containing a unique UUID (in 32-character hexdigit). BTW, '-tx_undo_actions' has not been implemented in Perinci::Tx::Manager, so the next release of Perinci::Tx::Manager will just implement '-tx_action_id'. 1.1.27 2012-08-28 (SHARYANTO) - Introduce status code 331. - Mark which status codes are in HTTP spec, which are introduced by us. 1.1.26 2012-08-22 (SHARYANTO) - Term change: call -> action. 1.1.25 2012-08-22 (SHARYANTO) [INCOMPATIBLE CHANGES] - Remove properties 'use' and 'req' in 'tx' feature. This is to make things simpler. use=>1 is redundant, if function follows transaction protocol it means you can use it in transaction. req=>1 is also removed, all functions should just require transaction and potentially return 'do_actions'. A simple wrapper can be created to execute those actions without transaction, if wanted. 1.1.24 2012-08-21 (SHARYANTO) [INCOMPATIBLE CHANGES] - Deprecate undo protocol, now undo should be implemented solely using transaction. - Revise transaction specification. Introduce protocol version (v) and bump it to v=2. Require 'tx' feature to specify protocol version. Incompatibilities include: 1) transaction now no longer uses undo protocol but adapts mechanism from Perinci::Sub::Gen::Undoable (which will also be deprecated as the mechanism is now elevated into standards); 2) TM object no longer needs to be passed to function, this should be safer; function now detects transaction using '-tx_action' special argument. [ENHANCEMENTS] - Specify TM's interface in more details (moved from Riap::Transaction, which now becomes shorter). - Specify steps for action, rollback, and crash recovery in more details, with example. 1.1.23 2012-08-14 (SHARYANTO) - Major rewrite of transaction specification. The main motivation is to remove the concept of 'steps' as this is a false dichotomy. Steps are actually functions themselves, the units of work are still functions. Eventually, a complex system will need to nest functions inside functions, to more than two levels of nesting. Why divide and limit into two levels (function-step)? So: - Everything is a bit clearer now - $tm interface is saner ($tm->call, easier to call function inside another) - No more mention of unused transaction status: e - New transaction statuses: v (rollback of undoing process), e (rollback of redoing process). - Recovery does not always means rollback (to R). For transactions in u and d status, we continue the undoing/redoing. For transactions in v and e status, we continue the rollback to final status C and U, respectively. 1.1.22 2012-08-09 (SHARYANTO) - tx: Describe the ordering of calls during undo/rollback/redo when there is nested call. The same function may be called twice or more with parts of undo/redo steps. - tx: In transaction mode, function now gets undo/redo data from the usual -undo_data special argument, like in non-transaction mode. Txm's API get_undo_steps() and get_redo_steps() are now removed. 1.1.21 2012-07-23 (SHARYANTO) [INCOMPATIBLE CHANGES] - function: Remove argument specification key 'src', use 'cmdline_src' instead. I think 'src' is too general. 1.1.20 2012-07-21 (SHARYANTO) - function: Add argument specification keys 'src' and 'cmdline_src'. 1.1.19 2012-06-22 (SHARYANTO) - Adjust transaction status labels. Final statuses are now in uppercase: C, R, U, X; while transient statuses are in lowercase: i, a, u, d, e. - Add response status 429 (too many requests). 1.1.18 2012-06-06 (SHARYANTO) - Refinements to transaction details. - Remove dependency clause 'undo_storage'. Add dependency clauses 'tmp_dir', 'trash_dir', 'undo_trash_dir'. 1.1.17 2012-05-31 (SHARYANTO) - Specify transactional system (Rinci::function::Transaction). Split specification for undo to Rinci::function::Undo and specify undo/redo protocol under transaction. 1.1.16 2012-05-03 (SHARYANTO) - Refine undo protocol documentation. Specify interaction with undo/transaction manager. '-undo_hint' is now replaced by '-undo_storage'. Declare 'undo_storage' dependency clause. 1.1.15 2012-05-02 (SHARYANTO) - No spec changes. [INCOMPATIBLE CHANGE] - Update Sah schema syntax (pre-0.03, [merge:X] -> [mergeX]) 1.1.14 2012-05-02 (SHARYANTO) [INCOMPATIBLE CHANGE] - Change 'exec' dependency clause to 'prog' (avoid possible confusion because 'exec' can imply that we need to execute the program; there can be other future dep clause for that). 1.1.13 2012-03-23 (SHARYANTO) [ENHANCEMENTS] - Each tag in 'tags' property can also be a tag metadata hash (for translatable message, etc). 1.1.12 2012-03-13 (SHARYANTO) [ENHANCEMENTS] - Add 'Rinci::result'. 1.1.11 2012-03-13 (SHARYANTO) [INCOMPATIBLE CHANGES] - package: Remove property 'pkg_version' (use 'entity_version' instead). [ENHANCEMENTS] - Add property 'entity_version'. 1.1.10 2012-02-28 (SHARYANTO) - function: arg spec 'aliases': Add alias spec 'schema' 1.1.9 2012-02-28 (SHARYANTO) [INCOMPATIBLE CHANGE] - function: Change (back) 'set' alias spec to 'code', in arg spec 'aliases' (for backward compatibility with 1.0) 1.1.8 2012-02-28 (SHARYANTO) [INCOMPATIBLE CHANGES] - function: Replace (back) 'alias_for' argument spec with 'cmdline_aliases'. I first used 'alias_for' so I can list each alias as a key in 'args' property. The goal is so I can get all argument names (- aliases) simply by doing a keys() on 'args' hash. And the goal of that is to add a 'complete_arg_name' Riap action which is more lightweight than a full 'meta' just to get argument names. Turns out that I don't need 'alias_for' just to support 'complete_arg_name', and also turns out that completing argument name needs more than just all argument names - aliases. You also need 'pos' information. So a full 'meta' is currently used. The reason I now revert to 'cmdline_aliases' style is because I don't want command-line aliases to become full/first-class argument. 1.1.7 2012-02-23 (SHARYANTO) - package: Add property 'pkg_version'. 1.1.6 2012-02-21 (SHARYANTO) - function: Add 'alias_for' argument specification. 1.1.5 2012-02-10 (SHARYANTO) - No spec changes. - Add Rinci::Schema. 1.1.4 2012-02-01 (SHARYANTO) - Declare that 1.1 series might introduce minor backward compatibility problems between revisions. - Rename 'arg_pass_style' and 'result_envelope' to the old (Sub::Spec-era) 'args_as' and 'result_naked'. New names are not better. - Code entity URI now moved to Riap specification as the 'riap' URI scheme. - Some minor revisions. 1.1.3 2012-01-27 (SHARYANTO) - Change syntax of code entity URI, from 'Pkg.SubPkg.func' to 'pm:/Pkg/SubPkg/func' (or 'py:', 'php:', and so on). - Some minor revisions like wording and paragraph reorganization. 1.1.2 2012-01-19 (SHARYANTO) - Add 'default_lang' property. Add guidelines on what to put in 'summary' and 'description'. - variable: Add 'schema' property. 1.1.1 2012-01-18 (SHARYANTO) - No spec changes. Update module names (Rinci::HTTP -> Riap, Rias -> Perinci). Add documents Rinci::Upgrading and Rinci::Tutorial (stub). 1.1.0 2012-01-15 (SHARYANTO) - First release. Spun off from Sub::Spec. [INCOMPATIBLE CHANGES FROM Sub::Spec 1.0.x] - Terminology: 'spec clause' becomes 'property'. This is to avoid confusion with 'clause' as used in Sah schema language. - Default text markup format changed from Org to Markdown, but a new properties 'text_markup' is added to allow specifying 'org' or 'markdown' (or 'none'). - 'v' clause is now required to declare Rinci spec version (with value 1.1, if unspecified then assumed spec is old Sub::Spec 1.0). - function: Incompatible changes to 'args' and 'result' properties; 'args' is now a hash of arg names and arg *specs* (instead of arg schemas). 'result' is now a hash of data, instead of schema. The purpose is to keep Sah schema clean from custom, non-schema-related schema clauses, like arg_* (thus requiring custom Sah extensions, etc). Mixing them into schemas was not the right way. - function: property 'type' removed, replaced with 'is_func', 'is_meth', 'is_static_meth'. This is because a single subroutine/function can act as all. - function: Other properties which are also removed/replaced: 'timeout' (non-core), 'retry' (non-core), 'scope' (can be replaced by using tags), 'result_naked' (replaced by 'result_envelope'), 'args_as' (replaced by 'arg_pass_style'). - function: property 'deps': terminology change ('dep clause' -> 'dep type'), rename dep types: 'sub' -> 'func', 'mod' -> 'perl_module'. [NEW FEATURES FROM Sub::Spec 1.0.x] - Keys beginning with "_" are allowed and ignored. This can be used to store extra information. - New properties: 'tags', 'links', 'examples'. Rinci-1.1.43/LICENSE0000644000175000017500000004366412256561363011333 0ustar u1u1This software is copyright (c) 2013 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. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2013 by Steven Haryanto. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2013 by Steven Haryanto. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Rinci-1.1.43/MANIFEST0000644000175000017500000000055312256561363011445 0ustar u1u1Changes LICENSE MANIFEST MANIFEST.SKIP META.json META.yml Makefile.PL README dist.ini lib/Rinci.pm lib/Rinci.pod lib/Rinci/Transaction.pod lib/Rinci/Undo.pod lib/Rinci/Upgrading.pod lib/Rinci/function.pod lib/Rinci/package.pod lib/Rinci/result.pod lib/Rinci/variable.pod t/00-compile.t t/release-pod-coverage.t t/release-pod-syntax.t t/release-rinci.t weaver.ini Rinci-1.1.43/lib/0000755000175000017500000000000012256561363011057 5ustar u1u1Rinci-1.1.43/lib/Rinci.pm0000644000175000017500000000173212256561363012464 0ustar u1u1package Rinci; our $VERSION = '1.1.43'; # VERSION 1; # ABSTRACT: Language-neutral metadata for your code __END__ =pod =encoding UTF-8 =head1 NAME Rinci - Language-neutral metadata for your code =head1 VERSION version 1.1.43 =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci.pod0000644000175000017500000004431512256561363012636 0ustar u1u1package Rinci; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: Language-neutral metadata for your code entities __END__ =pod =encoding UTF-8 =head1 NAME Rinci - Language-neutral metadata for your code entities =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION 1.1 =head1 ABSTRACT This document describes B, a set of extensible, language-neutral metadata specifications for your code (functions/methods, variables, packages, classes, and so on). Rinci allows various helper tools, from code generator to web middleware to documentation generator to other protocols, to act on your code, making your life easier as a programmer. Rinci also allows better interoperability between programming languages. Rinci is geared towards dynamic scripting languages like Perl, Python, Ruby, PHP, JavaScript, but is not limited to those languages. =head1 STATUS The 1.1 series does not guarantee full backward compatibility between revisions, so caveat implementor. However, major incompatibility will bump the version to 1.2 or 2.0. =head1 TERMINOLOGIES The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. B is a set of specifications of metadata for your code entities. Each different type of code entity, like function/method, variable, namespace, etc, has its own metadata specification. B is a defhash (see L). Each specification will specify what B should be supported. So the L specification will describe metadata for functions/methods, L will describe metadata for namespace/package, and so on. Rinci defines properties pertaining to documentation (like C, C, C, C), function argument and return value validation (C and C), dependencies (C), standardized feature description (C), also a few conventions/protocols for doing stuffs like undo (others like callback/progress report will follow). Basically anything that can describe the code entity. The specification is extensible: you can define more properties, or more deps, or more features. Since defhash can contain keys that are ignored (those that start with underscore, C<_>), extra information can be put here. =head1 WHAT ARE THE BENEFITS OF RINCI? By adding Rinci metadata to your code, you can write/use tools to do various things to your program. Rinci is designed with code generation and function wrapping in mind. At the time of this writing, in Perl there exists several tools (mostly modules under L namespace) to do the following: =over 4 =item * L Wrap functions with a single generated function that can do the following: validate input (using information from the C property), validate return value (the C property), add execution time-limiting (C), add automatic retries (C), interactive confirmation, logging, and more. =item * L A replacement for L or L if your functions are equipped with Rinci metadata. Automatically provide export tags (using information in the C property). Can automatically wrap functions using Perinci::Sub::Wrapper when exporting. =item * Perinci::To::* modules Convert metadata to various other documents, for example L to generate documentation. =item * L L command-line client. Call local/remote functions. Automatically convert command-line options/arguments to function arguments. Generate help/usage message (for C<--help>). Check dependencies (e.g. you can specify that in order to run your functions, you need some executables/other functions to exist, an environment variable being set, and so on), perform bash shell completion (using L). =item * L A L application (a set of PSGI middlewares, really) to serve metadata and function call requests over HTTP, according to the L protocol. =item * L An alternative for L for REST-style service. =item * L Use remote packages and import their functions/variables transparently like you would use local Perl modules. The remote server can be any Riap-compliant service, even when implemented in other languages. =item * C Since Rinci metadata are just normal data structure, they can be easily generated. The Perinci::Sub::Gen::* Perl modules can generate functions as well as their metadata, for example to access table data (like from a regular array or from a SQL database). =back More tools will be written in the future. =head1 RINCI VS ... Some features offered by Rinci (or Rinci tools) are undoubtedly already offered by your language or existing language libraries. For example, for documentation Perl already has POD and Python has docstrings. There are numerous libraries for argument validation in every language. Python has decorators that can be used to implement various features like argument validation and logging. Perl has subroutine attributes to stick additional metadata to your subroutines and variables. And so on. The benefits that Rinci offer include richer metadata, language neutrality, extensibility, and manipulability. B. Rinci strives to provide enough metadata for tools to do various useful things. For example, the C and C properties support translations. Argument specification is pretty rich, with a quite powerful and flexible schema language. B. You can share metadata between languages, including documentation and rules for argument validation. Perl 6 allows very powerful argument validation, for example, but it is language-specific. With Rinci you can easily share validation rules and generate validators in Perl and JavaScript (and other target languages). B. Being a normal data structure, your Rinci metadata is easier to manipulate (clone, merge, modify, export, what have you) as well as access (from your language and others). Perl's POD documentation is not accessible from the language (but Perl 6's Pod and Python docstrings are, and there are certainly tools to parse POD). On the other hand, Python docstrings are attached in the same file with the function, while with Rinci you can choose to separate the metadata into another file more easily. B. If you stack multiple decorators in Python, for example, it usually results in wrapping your Python function multiple times, which can add overhead. A single wrapper like L, on the other hand, uses a single level of wrapping to minimize subroutine call overhead. B. There is no reason why Rinci metadata has to compete against existing features from language/libraries. A code generator for Rinci metadata can generate code that utilize those features. For example, the C property can be implemented in Python using decorator, if you want. Rinci basically just provides a way for you to express desired properties/constraints/behaviours, separate from the implementation. A tool is free to implement those properties using whatever technique is appropriate. =head1 SPECIFICATION Note: Examples are usually written in Perl, but this does not mean they only apply to a particular language. =head2 Terminologies B, or just B for short, are elements in your code that can be given metadata. Currently supported entities are function/method, namespace/package, and variable. Other entities planned to be supported: class, object, library, application. =head2 Specification common to all metadata This section describes specification common to all kinds of Rinci metadata. B. The specification does not specify where to put metadata in: it might be put alongside the code, separated in another source code, encoded in YAML/JSON, put in database, or whatever. It is up to the tools/implementations to provide the mechanism. If you use L in Perl, there is a great deal of flexibility, you basically can do all of the above, even split the metadata in several files. See its documentation for more details. B. Below are properties common to all metadata: =head3 Property: v => FLOAT (required) From DefHash. Declare specification version. This property is required. It should have the value of 1.1. If C is not specified, it is assumed to be 1.0 and metadata is assumed to be the old, Sub::Spec 1.0.x metadata. Example: v => 1.1 =head3 Property: entity_v => STR Specify entity version (like package or function version). This is version as in software implementation version, not to be confused with C which is the metadata specification version (1.1). Example: entity_v => 0.24 In Perl, modules usually put version numbers in package variable called C<$VERSION>. If not set, tools like L automatically fills this property from that variable, to relieve authors from manually setting this property value. =head3 Property: default_lang => STR From DefHash. Specify default language used in the text properties like C and C. Default is 'en_US'. To specify translation texts in other languages, you can use C, e.g.: summary => "Perform the foo ritual", "summary.alt.lang.id_ID" => "Laksanakan ritual foo", =head3 Property: name => STR From DefHash. The name of the entity. Useful when aliasing entity (and reusing the metadata) and wanting to find out the canonical/original entity. Examples: name => 'foo' name => '$var' # only in languages where variables have prefix =head3 Property: summary => STR From DefHash. A one-line summary. It should be plain text without any markup. Please limit to around 72 characters. Example: # in variable metadata for $Answer summary => 'The answer to the question: what is the meaning of life' # in function metadata foo summary => 'Perform the foo ritual', For variable metadata, it should describe what the variable contain. You do not need to say "Contains ..." or "A variable that ..." since that is redundant; just say directly the content of the variable (noun). You also do not need to say what kinds of values the variable should contain, like "An integer, answer to the ..." or "..., should be between 1..100" since that should go to the C property. For function metadata, it should describe what the function does. Suggestion: use active, bare infinitive verb like in the example (not "Performs ..."). Avoid preamble like "This function ..." or "Function to ..." since that is redundant. Also avoid describing the arguments and its values like "..., accepts a single integer argument" as that should go to the C property. To specify translations in other language, use the L. Or change the C property. Examples: # default language is 'en_US' summary => 'Perform the foo ritual', "summary.alt.lang.id_ID" => 'Laksanakan ritual foo', # change default language to id_ID, so all summaries are in Indonesian, except # when explicitly set otherwise default_lang => 'id_ID', summary => 'Laksanakan ritual foo', "summary.alt.lang.en_US" => 'Perform the foo ritual', =head3 Property: tags => ARRAY OF (STR OR HASH) From DefHash. A list of tags, useful for categorization. Can also be used by tools, e.g. L in Perl uses the C property of the function metadata as export tags. Tag can be a simple string or a tag metadata hash. Example: # tag a function as beta tags => ['beta'] # the second tag is a detailed metadata tags => ['beta', { name => 'category:filtering', summary => 'Filtering', "summary.alt.lang.id_ID" => 'Penyaringan', } ] =head3 Property: description => STR From DefHash. A longer description text. The text should be in marked up in format specified by C and is suggested to be formatted to 78 columns. To avoid redundancy, you should mentioning things that are already expressed as properties, for example: return value of function (specify it in C property instead), arguments that the function accepts (C), examples (C), function's features (C) and dependencies/requirements (C). For function, description should probably contain a more detailed description of what the function does (steps, algorithm used, effects and other things of note). Example: { name => 'foo', summary => 'Perform the foo ritual', description => <, to specify translations in other language, use the L property. =head3 Property: links => ARRAY OF HASHES List to related entities or resources. Can be used to generate a SEE ALSO and/or LINKS sections in documentation. Each link is a defhash with the following keys: =over 4 =item * uri => STR (required) URI is used as a common syntax to refer to resources. If URI scheme is not specified, tools can assume that it is a C URI (see L). =item * title => STR A short plaintext title for the link. =item * description => STR From DefHash. A longer marked up text description for the link. Suggested to be formatted to 76 columns. =item * tags => ARRAY OF (STR OR HASH) From DefHash. Can be used to categorize or select links. For generating SEE ALSO sections, use the tag 'see'. =back Example: # links in the Bar::foo function metadata links => [ { url => "http://example.org/foo-with-bar-algo.html", title => "Article describing foo using Bar algorithm", }, { url => "../Bar2/", title => "Another implementation of the Bar algorithm", tags => ['see'], }, ], =head3 Property: x => ANY From DefHash. This property is used to store extended (application-specific) attributes, much like the C prefix in HTTP or email headers. This property can be used as an alternative to using underscore prefix (e.g. C<_foo>). Some processing tools strip properties/attributes that begin with underscores, so to pass extended metadata around, it might be more convenient to use the C property. It is recommended that you put an application prefix. Example: "x.myapp.foo" => "some value", Another example: "x.dux.strip_newlines" => 0, =head2 Entity-specific specifications Each entity-specific specification is described on a separate subdocument. Currently these specifications are defined: =over 4 =item * L - Metadata for functions/methods =item * L - Metadata for namespaces/packages =item * L - Metadata for variables =item * L - Function/method result metadata =back These specifications are planned or considered, but not yet defined: =over 4 =item * L - Metadata for classes =item * L - Metadata for objects =item * L - Metadata for applications =item * L - Metadata for libraries =item * L - Metadata for software distribution =item * L - Metadata for programming languages =item * L - Metadata for software authors =item * L - Metadata for software projects =item * L - Metadata for code repository (like git, svn) =back =head1 FAQ =head2 What does Rinci mean? Rinci is taken from Indonesian word B or B, meaning: specification, detail. =head2 Why use Sah for data schema? Sah is a flexible and extensible schema language, while still not being language-specific, making it easy for code generator tools to generate validator code in various target languages (Perl, Ruby, etc). =head1 HISTORY Below is the general history of the project and major changes to the specifications. For more detailed changes between releases, see the B file in the distribution. =head1 1.1 (Jan 2012) To clearly separate specification from implementation, rename specification from C to C (the namespace C is now used for the Perl implementation). Support code entities other than functions/methods. Bump specification version from 1.0 to 1.1 due to several incompatibilities like changed C and C properties, terminologies, defaults. Versioning property (C) now required. =head2 1.0 (Aug 2011) First release version of Sub::Spec. =head2 0.x (Feb-Aug 2011) Series of Sub::Spec drafts. =head2 Spanel project (2009-2010) I started using some metadata for API functions, calling them spec and putting them in %spec instead of in POD, so I can list and grab all the summaries easily as a single dump for API catalog (instead of having to parse POD from my source code files). Later on I kept adding more and more stuffs to this, from argument specification, requirements, and so on. =head1 SEE ALSO =head2 Related specifications L Sah schema language, L L =head2 Related ideas/concepts B<.NET attributes>, http://msdn.microsoft.com/en-us/library/z0w1kczw.aspx B, http://www.python.org/dev/peps/pep-0318/ , http://wiki.python.org/moin/PythonDecorators =head2 Other related links L, http://www.acmeism.org/ =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/0000755000175000017500000000000012256561363012123 5ustar u1u1Rinci-1.1.43/lib/Rinci/Upgrading.pod0000644000175000017500000000664012256561363014555 0ustar u1u1package Rinci::Upgrading; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: Upgrading from previous version of specification __END__ =pod =encoding UTF-8 =head1 NAME Rinci::Upgrading - Upgrading from previous version of specification =head1 VERSION version 1.1.43 =head1 DESCRIPTION This document gives guide on how to convert your metadata from older version. Examples are written in Perl, but are not limited to this language. =head1 UPGRADING FROM SUB::SPEC 1.0 TO RINCI 1.1 =head2 Upgrading function metadata In Perl, you can use L (which in turn is used by L) to automatically convert Sub::Spec 1.0 metadata to 1.1, so you can skip this document if you want. Terminology change: B<(sub) spec> is now called metadata: the reason is because there are now metadata for functions as well as other code entities like variables, packages, etc. B is now called B: the reason is to avoid confusion with L's clause. The C (spec version) property is required, add v => 1.1 to your function metadata. Each argument in the C property is now a hash, instead of a schema. Move the schema to the C key of the hash. To specify required argument, add C set to 1 to hash key. Move C Sah clause to C hash key. Move C Sah clause to C hash key. Move C Sah clause to C hash key. Example, change this: args => { # a required argument arg1 => ['int*', { summary => 'Blah ...', min => 1, max => 100, arg_pos => 0, }], # an optional argument arg2 => ['bool', { summary => 'Blah ...', default => 0, }], } Into this: args => { arg1 => { summary => 'Blah ...', schema => ['int*', {min=>1, max=>100}], req => 1, pos => 0, }, arg2 => { summary => 'Blah ...', schema => [bool => {default=>0}], }, } It is now possible to give summary and description to the schema as well as to the argument. Accordingly, the C property is also now a hash, instead of schema. Move the schema into the C hash key. =head2 Sub::Spec::HTTP For the most part, you only need to upgrade client and server library. Client library is now L and L. Server library is now C. Specification is now L and L. Terminology changes. B now becomes B. Some key names changed (shortened). Special headers are now prefixed with B instead of B. Log levels are now numeric only (1-6) instead of numeric/string. =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/variable.pod0000644000175000017500000000325612256561363014422 0ustar u1u1package Rinci::variable; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: Metadata for your variables __END__ =pod =encoding UTF-8 =head1 NAME Rinci::variable - Metadata for your variables =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION 1.1 =head1 INTRODUCTION This document describes metadata for variables. This specification is part of L. Please do a read up on it first, if you have not already done so. =head1 SPECIFICATION There is currently no metadata properties specific to variables. =head2 Property: schema => SCHEMA Specify the Sah schema that the variable's value must validate to. This can be used by a variable wrapper (getter/setter generator) tool to make sure that variable always contains valid values. Example: # metadata for variable $Meaning_Of_Life { ... summary => 'The meaning of life', schema => [int => {between => [1, 100]}, } =head1 FAQ =head1 SEE ALSO L =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/package.pod0000644000175000017500000000277112256561363014231 0ustar u1u1package Rinci::package; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: Metadata for your namespaces/packages __END__ =pod =encoding UTF-8 =head1 NAME Rinci::package - Metadata for your namespaces/packages =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION 1.1 =head1 INTRODUCTION This document describes metadata for namespaces/packages. This specification is part of L. Please do a read up on it first, if you have not already done so. =head1 SPECIFICATION B The term "package" from Perl 5 is used here, where it just means namespace. Perl also happens to use package to implement class, but metadata for classes will be specified in another specification (L). =head1 FAQ =head1 SEE ALSO L =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/Transaction.pod0000644000175000017500000007130712256561363015124 0ustar u1u1package Rinci::Transaction; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: A transactional system based on functions __END__ =pod =encoding UTF-8 =head1 NAME Rinci::Transaction - A transactional system based on functions =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION Rinci 1.1, protocol version 2 =head1 SPECIFICATION This document describes a transactional system based on functions, where several function calls participate in a single transaction. This transactional system has the following properties: =over 4 =item * Client/server architecture Transaction can be performed over L. Client can start more than one active transaction on the server. Each transaction-management request and the function calls are requested separately (each one is a separate Riap request). For more details on this, see L. =item * Undo/redo Committed transactions are still recorded in the database along with its undo information. Client can request to undo/redo the transactions. Thus the system is also an undo/redo system. =item * Relies on the functions for reliability/ACID properties Server or framework provides the transaction manager (TM), but each function acts as the resource manager (RM). It is the responsibility of the functions to maintain ACID properties while modifying resources. For best results, each function should be written carefully and tested extensively, and utilize a real, robust RM (like an RDBMS to store data or a transactional filesystem layer to read/modify files). In the absence of a real RM, some ACID properties like isolation and consistency might be compromised. For example: one transaction TX1 modifies a file in an ordinary (i.e. non-transactional) filesystem. Another transaction TX2 can see TX1's modification in the middle of uncommitted transaction (violates isolation principle). =back =head2 How transaction works The basic idea is that actions are performed by function calls. For each action, TM will call the function twice. First for getting undo information, and second for actually performing the action. The undo information can be used to perform rollback, undo, and redo. All functions performing actions in the transaction must be able to supply undo information. =head2 Function requirements Functions that participate in transaction must declare their C feature in the metadata. In addition, function must also be idempotent. features => { ... tx => {v=>2}, idempotent => 1, } Function must then follow the transaction protocol, described below. =head2 Transaction manager The transaction manager manages transaction data and performs actions as well as transaction management. For the sake of examples, our TM stores data in a SQL database (like SQLite) with the following tables: =over 4 =item * tx id (PK) summary ctime (creation time) commit_time status last_action_id -- in-progress action ID (for tx with status=i), or last -- processed action (for tx with other transient statuses) =item * do_action id (PK) tx_id (refers to tx(id)) ctime sp (savepoint name, UNIQUE(sp,tx_id)) f (function name) args (arguments, serialized) =item * undo_action id (PK) tx_id (refers to tx(id)) ctime f (function name) args (arguments, serialized) =back =head2 Transaction status A transaction can have one of these statuses. They will be fully explained in the following sections. Statuses having lowercase labels are transient statuses. Statuses having uppercase labels are final statuses. i (in-progress) a (aborted, pending rollback to R) R (rolled back) C (committed) u (committed, undoing) v (aborted undoing, pending rollback back to C) U (committed, undone) d (committed & undone, redoing) e (aborted redoing, pending rollback back to U) X (unresolvable/error) =head2 Transaction manager initialization User instantiates TM. TM sets up its data directory and performs cleanup and crash recovery. In cleanup, TM purges unneeded data, like data for rolled back transactions or committed transactions that have been around for too long. In crash recovery, TM looks at all crashed transactions and resolves them (either by performing rollback or roll forward). Crashed transactions are in-progress (C) transactions that have an in-progress action, or transactions having one of these statuses (all the other transient statuses): C, C, C, C, C. Crash recovery will be explained in the following sections below. TM also can perform rollback for in-progress transactions that have been around for too long without being committed or rolled back. =head2 Starting transaction User invokes C<< $tm->begin(tx_id => $tx_id) >>, providing a unique transaction ID C<$tx_id> as identifier for the transaction. C<$tx_id> is an arbitrary string with a length between 1 and 200 characters. User can also supply C, a textual description for the transaction. It should not be longer than 1024 characters. TM will create an entry for the transaction in its journal: BEGIN; INSERT INTO tx (id,summary,ctime,status) VALUES ($tx_id,$summary,$now,'i') COMMIT; As can be seen, initial transaction status is C (in-progress). Upon success, TM must return status 200. If transaction with that ID already exists, TM must return status 409, unless when the existing transaction is still on-going, in which case TM should just return 200. TM must return 400 if no $tx_id is given. TM can also return status 412 if there are already too many transactions being started, either globally on the server or for the particular client. =head2 Performing action 1) User performs action by invoking C<< $tm->action(f=>$fname, args=>$args) >> one or several times. Transaction status must be C. TM will first check whether function exists and supports transaction. If function does not exist, or does not support transaction, TM must return status 412. 2) TM records this action in its journal: BEGIN; INSERT INTO action (tx_id,ctime,f,args) VALUES ($tx_id,$now,$fname,JSON($args)); -- $act_id UPDATE tx SET last_action_id=$act_id WHERE id=$tx_id; COMMIT; 3) TM requests state checking and undo information to function, by calling the function using the arguments C<$args> and a special argument C<< -tx_action=>'check_state' >>. In addition TM also passes C<< -tx_v => N >> (the protocol version) and C<< -tx_action_id => UUID >> (a unique identifier to link between this call and the 'fix_state' call later). There are 3 possible states that the function must decide which we are in: =over 4 =item * fixed This is the final, desired state. When we are already in a fixed state, function must return status 304 (nothing to do). TM will then skip calling the function the second time to fix state, since there is nothing to fix. For example: [304, "File $path already exists"] # e.g., in a create_file() function [304, "User $u already does not exist"] # e.g., in a delete_user() function =item * fixable This is where the final, desired state has not been reached, but it is possible to reach it. When we are in this state, function must return status 200 with the result metadata C. The message should also describe what needs to be fixed. For example: [200, "Directory $path needs to be created", undef, {undo_actions => [rmdir => {path=>$path}]}] # e.g. in a mkdir() function [200, "User $u should be created with UID $uid", undef, {undo_actions => [delete_user=>{user=>$u}]}] # e.g. in create_user() =item * unfixable This is where the final, desired state has not been reached, and it is impossible or inappropriate for the function to fix into the fixed state. This state is used to avoid undoing what was not fixed by the function. If we are in this state, function should return status 412 (precondition failed). For example: [412, "Path $path exists but not a symlink"] # e.g. in setup_symlink() [412, "User $u exists but with different UID $cur_uid (needs $uid)"] =back If state is unfixable, or function returns other statuses (assumed as failure), TM stops the process and starts a rollback. C<< $tm->action() >> will return with the function's result. For example, let us use function C which can create a Unix user with an empty home directory if the user has not been created. This function utilizes several simpler functions: C to add entry to /etc/passwd and /etc/shadow, C to add entry to /etc/group and /etc/gshadow, C to create directory. Then there are also these functions for the undo actions: C to delete user entry in Unix passwd database, C to delete group entry in Unix group database, and C to remove directory. For C, the fixable state is that the user does not exist, the fixed state is that the user exists. For C, the fixable state is that user exists (additionally with the same UID as the one created previously), the fixed state is user does not exist, the unfixable state is user exists but with different UID. For C, the fixable state is that group does not exist, the fixed state is that the group exists. For C, the fixable state is that group exists (additionally with the same GID as the one created previously), the fixed state is group does not exist, the unfixable state is group exists but with different GID. For C, the fixable state is path does not exist, the fixed state is directory exists, and unfixable state is path exists but is not a directory. For C, the fixable state is directory exists and empty, the fixed state is path does not exist, the unfixable state is path exists but not a directory or directory is not empty. The C must be an array containing action information, in reverse order. Each action is a two-element array C<[$fname, $args]> where C<$fname> is name of a function (not necessarily the same function) and C<$args> its call arguments. For example, if user invokes C<< $tm->action(f=>'My::setup_unix_user', args=>{user=>'bob'}) >> and user C does not exist yet, function will return: [200, "OK", undef, {undo_actions=>[ ['My::deluser', {group=>'bob'}], ['My::delgroup', {group=>'bob'}], ['My::rmdir', {path=>'/home/bob'}], }] 4) TM records these undo actions in its journal: BEGIN; INSERT INTO undo_action (tx_id,ctime,action_id,f,args) VALUES ($tx_id,$now,$act_id,'My::deluser','{"group":"bob"}'); -- # $uact_id1 INSERT INTO undo_action (tx_id,ctime,action_id,f,args) VALUES ($tx_id,$now,$act_id,'My::delgroup','{"user":"bob"}'); -- # $uact_id2 INSERT INTO undo_action (tx_id,ctime,action_id,f,args) VALUES ($tx_id,$now,$act_id,'My::rmdir','{"path":"/home/bob"}'); -- # $uact_id3 COMMIT; 5) If we are in fixed state, this step is skipped. If we are in fixable state, TM calls function the second time, this time with C<< -tx_action => 'fix_state' >>. TM also passes C<-tx_v> and C<-tx_action_id> with the same value as the one passed previously during the 'check_state' call. Function must perform action to fix the state into the fixed state. In our example, C should create user and group C, and creates an empty directory C. Function must return status 200 on success. Other status will be interpreted as failure, in which case TM will stop the process and starts rollback. C<< $tm->action() >> will return with the function's result. Note: During the 'check_state' phase in step 3, function can also optionally return C in its result metadata, for example: [200, "OK", undef, {do_actions=>[ ['My::adduser', {group=>'bob'}], ['My::addgroup', {group=>'bob'}], ['My::mkdir', {path=>'/home/bob'}], undo_actions=>[ ['My::deluser', {group=>'bob'}], ['My::delgroup', {group=>'bob'}], ['My::rmdir', {path=>'/home/bob'}], }] In this case, instead of calling function the second time, TM will just call the actions provided by the function, using a nested C<< $tm->action(actions => $do_actions) >>. Step 4 will be skipped since each do action will provide its own undo actions. 6) If 'fix_state' phase in step 5 succeeds, the action is finished. TM marks this: BEGIN; UPDATE tx SET last_action_id=NULL WHERE id=$tx_id; COMMIT; TM is ready to process another action. =head3 Crash recovery Recovery rolls back interrupted in-progress transaction. See L for more details. If crash happens after step 1, transaction will not be marked as crash since C has not been set and no recovery is necessary. If crash happens after step 2 until 5, recovery will be performed by rollback. Details of rollback is explained in L. If crash happens after step 6, transaction will not be marked as crash since C is already unset and no recovery is necessary. =head2 Commit To commit transaction, user invokes C<< $tm->commit() >>. Transaction status must be C or C. If transaction status is C, transaction must be rolled back instead. TM will mark the transaction status as C (committed) and delete all entries in the C table since they are no longer needed: BEGIN; UPDATE tx SET status='C' WHERE id=$tx_id; DELETE FROM do_action WHERE tx_id=$tx_id; COMMIT; TM still stores the C entries for some time, to allow undo (and redo) of transactions. If transaction status is C, transaction should be rolled back instead of committed. Transaction status progress: i -> C =head2 Rollback of in-progress (status i) transaction If an action fails, or some other error happens, rollback will be performed by TM. Rollback can also be started by user using C<< $tm->rollback >>. TM marks transaction status to C (aborted). This will prevent other clients trying to add new actions to this transaction, since aborted transaction can longer accept new actions, it can only be rolled back. TM will then perform undo for each function, in reverse order, using the undo actions previously recorded in C table. The process is similar to performing action, except that: =over 4 =item * After rollback succeeds, transaction status is changed to C C means rolled back. These transactions can be discarded by the next cleanup process. =item * Undo actions are not recorded Since we do not rollback from the rollback process, but continue it. TM still calls function twice for each action (check_state + fix_state), but do not bother to record the undo actions returned by function in the check_state phase to its database. =item * Failure in rollback step will mark transaction status as C C means inconsistent/error. Transactions left in this state are probably half-done and thus inconsistent. We give up on these transactions and the next cleanup process can discard them. (TODO: Should there be an option to continue to the next action anyway? But this is not necessarily more robust or correct.) =back Transaction status progress: i -> a -> R # successful rollback i -> a -> X # failed rollback B. Continuing our previous example, in the C<< setup_unix_user(user=>'bob') >> action, there are 3 actions involved: ['My::adduser', {group=>'bob'}] ['My::addgroup', {group=>'bob'}] ['My::mkdir', {path=>'/home/bob'}] Suppose action 1 and 2 succeed, and the following undo actions have been recorded in C: ['My::deluser', {group=>'bob'}] # recorded with ID $ucall_id1 ['My::delgroup', {group=>'bob'}] # recorded with ID $ucall_id2 Suppose action 3 fails with status 500 (e.g. permission denied) and thus rollback is started. The following is the steps that happen during rollback. Actions will be processed in reverse order: C<$ucall_id2>, C<$ucall_id1>. 1) TM marks transaction status to aborted: BEGIN; UPDATE tx SET status='a', last_action_id=NULL WHERE id=$tx_id; COMMIT; TM performs action C. 2a) TM calls C the first time with C<< -tx_action => 'check_state' >>. TM also passes C<< -tx_is_rollback => 1 >> for informative purposes (some function can utilize this information to behave more robust, for example, to avoid failing the rollback process). TM does not record the C metadata returned, but observes the C. If function returns 304, step 2b is skipped and TM moves on to the next action. If function returns 200, TM continues to step 2b. If function returns other statuses, TM assumes rollback failure and marks transaction as C and ends the rollback process for this transaction. 2b) TM invokes C the second time to perform the action, passing C<< -tx_action => 'fix_state' >> and C<< -tx_is_rollback => 1 >>. Function sees that group exists (fixable state), deletes it, return status 200. 2c) TM sets transaction's C to C<$uact_id1> to mark that this action has been processed: BEGIN; UPDATE tx SET last_action_id=$ucall_id1 WHERE id=$tx_id; COMMIT; TM then continues to perform action C. 3a) Just like in step 2, TM invokes C the first time to check state. 3b) TM invokes C to perform the action. Function sees that user exists (fixable state), deletes it, return status 200. 3c) TM sets transaction's C to C<$uact_id2> to mark that this action has been processed: BEGIN; UPDATE tx SET last_action_id=$uact_id2 WHERE id=$tx_id; COMMIT; 4) TM completes the rollback process by setting transaction status to C. BEGIN; UPDATE tx SET status='R' WHERE id=$tx_id; COMMIT; By now the effect of the transaction has been nullified. =head3 * Crash recovery Recovery continues the interrupted rollback process. If crash happens after step 1, recovery will continue the rollback process. Rollback of aborted (status a) transaction is exactly the same as rollback of in-progress (status i) transaction, except that C is not reset. If crash happens after step 2a-2b, C is still unset, so the process resumes at step 2a. TM does not remember whether previously before crash the function has been executed (and cannot remember, the progress of the execution inside the function). This is the reason why function needs to be idempotent, because it is potentially executed twice by TM for the same action. If function has completed deleting the group before crash, C will return status 304 (fixed) and TM will skip step 2b. If function has not deleted the group before crash, C will return status 200 (fixable) and TM will execute step 2b. If crash happens after step 2c/3a-3b, C is set to C<$uact_id1>. Process will resume at step 3a, since $uact_id1 has been marked as done. If crash happens after step 3c, process will resume at step 4. If crash happens after step 4, no recovery is necessary since transaction has been rolled back completely. =head2 Undo TM allows undoing committed transaction, so the transaction system also serves as an undo/redo system. 1) User performs undo by invoking C<< $tm->undo(tx_id => $tx_id) >>, where C<$tx_id> is the ID of a committed transaction. If C<$tx_id> is not supplied, the client's newest committed transaction is used. TM will first check that transaction status is indeed C. 2) TM sets transaction status to C (undoing): BEGIN; UPDATE tx SET status='u' WHERE id=$tx_id; COMMIT; TM then performs actions specified in the C table. The process is similar to performing action, except: =over 4 =item * After undo succeeds, transaction status is changed to C C means committed but undone transaction. These transactions can be redone back to status C. =item * Undo actions are recorded in C table instead of C =item * Failure in undo step will cause transaction to roll back to status C =back Transaction status progress: C -> u -> U # successful undo C -> u -> v -> C # failed undo, rolled back to C Continuing our previous example, suppose our C<< setup_unix_user(user=>'bob') >> transaction has succeeded and been committed. The C table contains these entries: ['My::deluser', {group=>'bob'}] # recorded with ID $uact_id1 ['My::delgroup', {group=>'bob'}] # recorded with ID $uact_id2 ['My::rmdir', {path=>'/home/bob'}] # recorded with ID $uact_id3 Actions will be processed in reverse order: C<$uact_id3>, C<$uact_id2>, C<$uact_id1>. 3a) TM invokes C the first time with C<< -tx_action => 'check_state' >>. If directory has been filled by files/subdirectories, function will return 412 ("Cannot remove home directory, non-empty") and the undo process fails with this status. If directory exists and is still empty, function will return 200 (fixable state) and process continues. 3b) TM records the C result metadata returned by function to C table, for redo information. BEGIN; INSERT INTO do_action (tx_id,ctime,f,args) VALUES ($tx_id,$now,'My::mkdir', '{"path":"/home/bob"}'); # -- $ract_id1 COMMIT; 3c) TM invokes C the second time with C<< -tx_action => 'fix_state' >>. Function deletes directory and return 200. 3d) TM updates C to mark that this action has been processed: BEGIN; UPDATE tx SET last_action_id=$uact_id3 WHERE id=$tx_id; COMMIT; TM then continue to C<$uact_id2>. 4a) TM invokes C the first time with C<< -tx_action => 'check_state' >>. 4b) TM records undo_actions: BEGIN; INSERT INTO do_action (tx_id,ctime,f,args) VALUES ($tx_id,$now,'My::addgroup', '{"group":"bob"}'); # -- $ract_id2 COMMIT; 4c) TM invokes C the second time with C<< -tx_action => 'fix_state' >>. Function sees that group exists, deletes it, and returns 200. 4d) TM updates C: BEGIN; UPDATE tx SET last_action_id=$uact_id2 WHERE id=$tx_id; COMMIT; TM then continue to C<$uact_id1>. 5a) TM invokes C the first time with C<< -tx_action => 'check_state' >>. 5b) TM records undo_actions: BEGIN; INSERT INTO undo_action (tx_id,ctime,f,args) VALUES ($tx_id,$now,'My::adduser', '{"user":"bob"}'); # -- $ract_id3 COMMIT; 5c) TM invokes C the second time with C<< -tx_action => 'fix_state' >>. Function sees that user exists, deletes it, and returns 200. 5d) TM updates C: BEGIN; UPDATE tx SET last_action_id=$uact_id1 WHERE id=$tx_id; COMMIT; 6) TM completes the undo process by setting transaction status to C: BEGIN; UPDATE tx SET status='U', last_action_id=NULL WHERE id=$tx_id; COMMIT; =head3 Crash recovery Recovery rolls back interrupted undoing process so that transaction status is back to C (committed). For more details, refer to L. If crash happens before finishing step 2, no recovery is necessary. If crash happens after step 2-3c, recovery resumes from step 3a since C is still unset. That is why C needs to be idempotent and can check state, since it is potentially executed (step 3c) twice, before and after recovery. If crash happens after step 3d-4c, recovery recovery resumes from step 4a since C is set to C<$uact_id3>. If crash happens after step 4d-5c, recovery resumes from step 5a since C is set to C<$uact_id2>. If crash happens after step 5d, recovery resumes from step 6. =head2 Rolling back the undoing (status u) transaction If undo fails in the middle, rollback will happen. TM marks transaction status from C to C, this differentiates between an undo process in progress (in which case recovery should continue it until status is C) and a failed undo process (in which case recovery should rolls it back to status C). TM will then perform actions from the C table. The process is similar to rollback of in-progress (status i) transaction, except that after rollback succeeds, transaction status is set to C. If rollback fails, transaction status is set to C. Transaction status progress: u -> v -> C # rollback succeeds u -> v -> X # rollback fails =head3 Crash recovery Recovery continues the rollback process. =head2 Redo An undone transaction (status C) can be redone back to C. To do this, user invokes C<< $tm->undo(tx_id => $tx_id) >>, where C<$tx_id> is the ID of an undone transaction. If C<$tx_id> is not supplied, the client's newest undone transaction is used. TM will first check that transaction status is indeed C. TM will then set transaction status to C (redoing): BEGIN; UPDATE tx SET status='d' WHERE id=$tx_id; COMMIT; This will prevent other clients trying to redo the same transaction. TM will then process actions found in C table, just like when performing normal action. Transaction status progress: U -> d -> C =head3 Crash recovery Recovery rolls back the redoing process. See L. =head2 Rolling back a redoing (status d) transaction If redo fails in the middle, rollback will happen. TM marks transaction status from C to C (failed redo). This will differentiate between a redo process in progress (in which case recovery should continue it until status is C) and a failed redo process (in which case recovery should rolls it back to status C). TM will perform actions from the C table. The process is similar to rollback of an in-progress (status i) transaction, except that after rollback succeeds, transaction status is set to C. If rollback fails, TM will set transaction status to C. Transaction status progress: d -> e -> U # rollback succeeds d -> e -> X # rollback fails =head3 Crash recovery Recovery continues the rollback process. =head2 Cleanup Cleanup is done at TM startup and at regular intervals. TM should delete (forget) all C and U transactions that are too old, or keep the number of those transactions under a certain limit, according to its settings. As soon as those transactions are deleted, they can no longer be undone/redone, since the undo actions data has been deleted too. The cleanup process also deletes all X transactions, since they cannot be resolved anyway (TODO: perhaps some retry mechanism can be applied, if desired?) Cleanup process also deletes all R transactions. Cleanup process can also roll back any transactions with status C that have been going for too long without being committed/rolled back. =head2 Savepoint Basically savepoint is just a label in the C table. To mark a savepoint, user invokes C<< $tm->savepoint(sp_id=>$sp_id) >> where C<$sp_id> is an arbitrary string from 1-64 characters. It must be unique within the transaction. If the same savepoint is used, the old savepoint is replaced by the new one. To release (forget) a savepoint, user invokes C<< $tm->release_savepoint(sp_id=>$sp_id) >>. It just clears the label in the C table. Rollback to a savepoint is just a normal rollback process, except we stop after finishing the undo actions of the corresponding action with the savepoint, and transaction status is set back to C. If savepoint is unknown (or marked before any action, which is effectively the same), we rollback everything in the transaction. =head2 Discard User can optionally do a cleanup of her transactions by issuing C<< $tm->discard(tx_id=>$tx_id) >> or C<< $tm->discard_all >>. Transactions that can be discarded are those with the final statuses: C, C, C. =head1 FAQ =head2 Why is this useful? The protocol is a pretty generic and simple way to build transactional system, even on heterogenous, multiuser environment. If the functions are written carefully, the system can be reliable. And even if some of the ACID properties are compromised due to lack of real RM, the system is still useful for its undo/redo capability. =head2 What are the drawbacks? The reliability of the system rests on the reliability of each involved function. One buggy function can break the transaction. =head2 What about non-undoable actions? Non-undoable actions (like sending an email, permanently deleting files) should be executed outside the scope of transaction. =head1 SEE ALSO Transaction behavior is largely based on PostgreSQL. Related specifications: L, L Implementations: L =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/Undo.pod0000644000175000017500000002173612256561363013545 0ustar u1u1package Rinci::Undo; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: (DEPRECATED) Protocol for undo operations in functions __END__ =pod =encoding UTF-8 =head1 NAME Rinci::Undo - (DEPRECATED) Protocol for undo operations in functions =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION 1.1 =head1 STATUS This protocol (riundo for short) is now deprecated in favor of L (ritx for short) for several reasons: =over 4 =item * riundo is inherently unreliable Undo information is returned by function I the function has performed the action. If function dies in the middle of action, client does not have the information to undo the (partially completed) action. That is why in ritx, the TM asks the function first for undo information before asking the function to perform its action. =item * ritx does not limit using the same function for undo In riundo, we must call the same function (passing the previously obtained undo data from the that function) to undo the information. This is sometimes slightly cumbersome. The undo action might be provided by other functions, but we still have to go through the same function first. =item * ritx can also implement undo/redo So there is no need for maintaining two specifications. =back =head1 SPECIFICATION This document describes the Rinci undo protocol. This protocol must be followed by functions that claim that they support undo (have their C C set to true). Such functions are from here on called I (or just function, unless when ambiguous). The protocol is basically the non-OO version of the command pattern, a design pattern most commonly used to implement undo/redo functionality. In this case, each function behaves like a command object. You pass a special argument C<-undo_action> with the value of C and C to execute or undo a command, respectively. For C and C, the same set of arguments are passed. =head2 Requirements Function MUST check special argument C<-undo_action> before it checks other arguments. Function MUST at least support the following undo action: C, C. On unsupported/unknown undo action, function MUST return status 400, with message like "Unsupported undo action". If C<-undo_action> is not set, it means caller does not care about undo. Undoable function should execute as any normal function. =head2 Performing 'do' To indicate that we need undo, we call function by passing special argument C<-undo_action> with the value of C. Function should perform its operation and save undo data along the way. If C<-undo_action> is not passed or false/undef, function should assume that caller does not need undo later, so function need not save any undo data. After completing operation successfully, function should return status 200, the result, and undo data. Undo data is returned in the result metadata (the fourth element of result envelope), example: [200, "OK", $result, {undo_data=>$undo_data}] Undo data should be serializable so it is easy to be made persistent if necessary (e.g. by some undo/transaction manager). =head2 Performing 'undo' To perform an undo, caller must call the function again with the same previous arguments, except C<-undo_action> should be set to C and C<-undo_data> set to undo data previously given by the function. Function should perform the undo operation using the undo data. Upon success, it must return status 200, the result, and an undo data (in other words, redo data, since it can be used to undo the undo operation). =head2 Performing 'redo' To perform redo, caller can call the function again with <-undo_action> set to C and C<-undo_data> set to the redo data given in the undo step. Or, alternatively, caller can just perform a normal do (see above). An example: $SPEC{setenv} = { v => 1.1, summary => 'Set environment variable', args => { name => {req=>1, schema=>'str*'}, value => {req=>1, schema=>'str*'}, }, features => {undo=>1}, }; sub setenv { my %args = @_; my $name = $args{name}; my $value = $args{value}; my $undo_action = $args{-undo_action} // ''; my $undo_data = $args{-undo_data}; my $old; if ($undo_action) { # save original value and existence state $old = [exists($ENV{$name}), $ENV{$name}]; } if ($undo_action eq 'undo') { if ($undo_data->[0]) { $ENV{$name} = $undo_data->[1]; } else { delete $ENV{$name}; } } else { $ENV{$name} = $value; } [200, "OK", undef, $undo_action ? {undo_data=>$old} : {}]; } The above example declares an undoable command C to set an environment variable (C<%ENV>). To perform command: my $res = setenv(name=>"DEBUG", value=>1, -undo_action=>"do"); die "Failed: $res->[0] - $res->[1]" unless $res->[0] == 200; my $undo_data = $res->[3]{undo_data}; To perform undo: $res = setenv(name=>"DEBUG", value=>1, -undo_action="undo", -undo_data=>$undo_data); die "Can't undo: $res->[0] - $res->[1]" unless $res->[0] == 200; After this undo, DEBUG environment variable will be set to original value. If it did not exist previously, it will be deleted. To perform redo: my $redo_data = $res->[3]{undo_data}; $res = setenv(name=>"DEBUG", value=>1, -undo_action="undo", -undo_data=>$redo_data); or you can just do: $res = setenv(name=>"DEBUG", value=>1, -undo_action="do"); =head2 Saving undo data in external storage Although the complete undo data can be returned by the function in the C result metadata property, sometimes it is more efficient to just return a pointer to said undo data, while saving the actual undo data in some external storage. For example, if a function deletes a big file and wants to save undo data, it is more efficient to move the file to trash directory and return its path as the undo data, instead of reading the whole file content and its metadata to memory and return it in C result metadata. Functions which require undo trash directory should specify this in its metadata, through the C dependency clause. For example: deps => { ... trash_dir => 1, } When calling function, caller needs to provide path to undo trash directory via special argument C<-trash_dir>, for example: -trash_dir => "/home/.trash/2fe2f4ad-a494-0044-b2e0-94b2b338056e" =head2 What about non-undoable actions? Like in real life, not all actions are undoable. Examples of undoable/irreversible actions include wiping a file/directory (more generally speaking, any action to permanently delete/destroy something, without backing up the data first), sending an email (more generally speaking, any action that is sent to an external entity beyond our control, unless that external entity provides a way to undo the action). An undoable function MUST NOT mix undoable and non-undoable actions. For example: safe_delete(file=>'/path/to/file'); # puts file into Trash, undoable action safe_delete(file=>'/path/to/file', permanent=>1); # deletes file, non-undoable The C function above mixes undoable action (putting a file into Trash directory) and non-undoable action (permanently deleting a file without putting it in Trash). Without domain knowledge of the function, a caller cannot know whether a call will be undoable or not. This will also prevent the function from participating in a transaction, because transaction requires function call to always be undoable, for rollback purpose. The solution is to separate non-undoable action in another function, for example: trash(file=>'/path/to/file'); # undoable, can execute inside transaction delete(file=>'/path/to/file'); # non-undoable, executes outside transaction empty_trash(); # non-undoable, executes outside transaction The non-undoable function is also non-transactional (it operates outside the scope of a transaction). But it can still be idempotent. And it can manipulate the transactions if it needs too. In the example, the empty_trash() function instructs the transaction manager to discard the trash() transactions, since after the trash is emptied, the trash() transactions cannot be undone anyway. =head1 SEE ALSO Related specifications: L =head1 HOMEPAGE Please visit the project's homepage at L. =head1 SOURCE Source repository is at L. =head1 BUGS Please report any bugs or feature requests on the bugtracker website L 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. =head1 AUTHOR Steven Haryanto =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 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. =cut Rinci-1.1.43/lib/Rinci/result.pod0000644000175000017500000000721412256561363014151 0ustar u1u1package Rinci::result; # just to make PodWeaver happy # VERSION 1; # ABSTRACT: Function/method result metadata __END__ =pod =encoding UTF-8 =head1 NAME Rinci::result - Function/method result metadata =head1 VERSION version 1.1.43 =head1 SPECIFICATION VERSION 1.1 =head1 INTRODUCTION This document describes metadata for function/method result. This specification is part of L. Please do a read up on it first, if you have not already done so. =head1 SPECIFICATION There are currently several properties being used: =head2 Property: undo_data => ANY (DEPRECATED) Explained in C feature section in L. =head2 Properties: func.* => ANY These properties allow function to return extra stuffs. Usually done to avoid breaking format of existing result (to maintain API compatibility). The attributes after C is up to the respective function. An example is the C function in the L Perl module. The function returns C<$args> but from v0.26 it also wants to give hints about whether or not there are missing arguments. It can do this via C result metadata. =head2 Properties: cmdline.* Interpreted by L. See its documentation for more detail. =head2 Property: logs => ARRAY OF HASH Store log of events happening to this result, stored chronologically (older first). Each log should be a hash which should have at least the following keys: C