Rinci-1.1.86/0000775000175000017500000000000013212744366010321 5ustar u1u1Rinci-1.1.86/README0000644000175000017500000000167013212744366011203 0ustar u1u1NAME Rinci - Language-neutral metadata for your code VERSION This document describes version 1.1.86 of Rinci (from Perl distribution Rinci), released on 2017-12-09. HOMEPAGE Please visit the project's homepage at . SOURCE Source repository is at . BUGS Please report any bugs or feature requests on the bugtracker website 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 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. Rinci-1.1.86/dist.ini0000644000175000017500000000024213212744366011761 0ustar u1u1version=1.1.86 name=Rinci [@Author::PERLANCAR] :version=0.59 [Prereqs] [Prereqs / DevelopX_spec] -phase=develop -relationship=x_spec DefHash=1.0.6 Sah=0.9.36 Rinci-1.1.86/Makefile.PL0000644000175000017500000000205413212744366012272 0ustar u1u1# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.008. use strict; use warnings; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Language-neutral metadata for your code", "AUTHOR" => "perlancar ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Rinci", "LICENSE" => "perl", "NAME" => "Rinci", "PREREQ_PM" => {}, "TEST_REQUIRES" => { "File::Spec" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Test::More" => 0 }, "VERSION" => "1.1.86", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "File::Spec" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Test::More" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Rinci-1.1.86/META.yml0000644000175000017500000003123213212744366011571 0ustar u1u1--- abstract: 'Language-neutral metadata for your code' author: - 'perlancar ' build_requires: File::Spec: '0' IO::Handle: '0' IPC::Open3: '0' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Rinci resources: bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Rinci homepage: https://metacpan.org/release/Rinci repository: git://github.com/perlancar/perl-Rinci.git version: 1.1.86 x_Dist_Zilla: perl: version: '5.024000' plugins: - class: Dist::Zilla::Plugin::GatherDir config: Dist::Zilla::Plugin::GatherDir: exclude_filename: [] exclude_match: [] follow_symlinks: 0 include_dotfiles: 0 prefix: '' prune_directory: [] root: . name: '@Author::PERLANCAR/@Filter/GatherDir' version: '6.008' - class: Dist::Zilla::Plugin::PruneCruft name: '@Author::PERLANCAR/@Filter/PruneCruft' version: '6.008' - class: Dist::Zilla::Plugin::ManifestSkip name: '@Author::PERLANCAR/@Filter/ManifestSkip' version: '6.008' - class: Dist::Zilla::Plugin::MetaYAML name: '@Author::PERLANCAR/@Filter/MetaYAML' version: '6.008' - class: Dist::Zilla::Plugin::License name: '@Author::PERLANCAR/@Filter/License' version: '6.008' - class: Dist::Zilla::Plugin::PodCoverageTests name: '@Author::PERLANCAR/@Filter/PodCoverageTests' version: '6.008' - class: Dist::Zilla::Plugin::PodSyntaxTests name: '@Author::PERLANCAR/@Filter/PodSyntaxTests' version: '6.008' - class: Dist::Zilla::Plugin::ExtraTests name: '@Author::PERLANCAR/@Filter/ExtraTests' version: '6.008' - class: Dist::Zilla::Plugin::ExecDir name: '@Author::PERLANCAR/@Filter/ExecDir' version: '6.008' - class: Dist::Zilla::Plugin::ShareDir name: '@Author::PERLANCAR/@Filter/ShareDir' version: '6.008' - class: Dist::Zilla::Plugin::MakeMaker config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: '@Author::PERLANCAR/@Filter/MakeMaker' version: '6.008' - class: Dist::Zilla::Plugin::Manifest name: '@Author::PERLANCAR/@Filter/Manifest' version: '6.008' - class: Dist::Zilla::Plugin::ConfirmRelease name: '@Author::PERLANCAR/@Filter/ConfirmRelease' version: '6.008' - class: Dist::Zilla::Plugin::ExecDir name: '@Author::PERLANCAR/ExecDir script' version: '6.008' - class: Dist::Zilla::Plugin::PERLANCAR::BeforeBuild name: '@Author::PERLANCAR/PERLANCAR::BeforeBuild' version: '0.590' - class: Dist::Zilla::Plugin::Rinci::AbstractFromMeta name: '@Author::PERLANCAR/Rinci::AbstractFromMeta' version: '0.10' - class: Dist::Zilla::Plugin::PodnameFromFilename name: '@Author::PERLANCAR/PodnameFromFilename' version: '0.02' - class: Dist::Zilla::Plugin::PERLANCAR::EnsurePrereqToSpec name: '@Author::PERLANCAR/PERLANCAR::EnsurePrereqToSpec' version: '0.05' - class: Dist::Zilla::Plugin::PERLANCAR::MetaResources name: '@Author::PERLANCAR/PERLANCAR::MetaResources' version: '0.03' - class: Dist::Zilla::Plugin::CheckChangeLog name: '@Author::PERLANCAR/CheckChangeLog' version: '0.04' - class: Dist::Zilla::Plugin::CheckMetaResources name: '@Author::PERLANCAR/CheckMetaResources' version: '0.001' - class: Dist::Zilla::Plugin::CopyrightYearFromGit name: '@Author::PERLANCAR/CopyrightYearFromGit' version: '0.003' - class: Dist::Zilla::Plugin::IfBuilt name: '@Author::PERLANCAR/IfBuilt' version: '0.03' - class: Dist::Zilla::Plugin::MetaJSON name: '@Author::PERLANCAR/MetaJSON' version: '6.008' - class: Dist::Zilla::Plugin::MetaConfig name: '@Author::PERLANCAR/MetaConfig' version: '6.008' - class: Dist::Zilla::Plugin::Authority name: '@Author::PERLANCAR/Authority' version: '1.009' - class: Dist::Zilla::Plugin::OurDate name: '@Author::PERLANCAR/OurDate' version: '0.03' - class: Dist::Zilla::Plugin::OurDist name: '@Author::PERLANCAR/OurDist' version: '0.02' - class: Dist::Zilla::Plugin::PERLANCAR::OurPkgVersion name: '@Author::PERLANCAR/PERLANCAR::OurPkgVersion' version: '0.04' - class: Dist::Zilla::Plugin::PodWeaver config: Dist::Zilla::Plugin::PodWeaver: finder: - ':InstallModules' - ':ExecFiles' plugins: - class: Pod::Weaver::Plugin::EnsurePod5 name: '@CorePrep/EnsurePod5' version: '4.013' - class: Pod::Weaver::Plugin::H1Nester name: '@CorePrep/H1Nester' version: '4.013' - class: Pod::Weaver::Section::Name name: '@Author::PERLANCAR/Name' version: '4.013' - class: Pod::Weaver::Section::Version name: '@Author::PERLANCAR/Version' version: '4.013' - class: Pod::Weaver::Section::Region name: '@Author::PERLANCAR/prelude' version: '4.013' - class: Pod::Weaver::Section::Generic name: SYNOPSIS version: '4.013' - class: Pod::Weaver::Section::Generic name: DESCRIPTION version: '4.013' - class: Pod::Weaver::Section::Generic name: OVERVIEW version: '4.013' - class: Pod::Weaver::Section::Collect name: ATTRIBUTES version: '4.013' - class: Pod::Weaver::Section::Collect name: METHODS version: '4.013' - class: Pod::Weaver::Section::Collect name: FUNCTIONS version: '4.013' - class: Pod::Weaver::Section::Leftovers name: '@Author::PERLANCAR/Leftovers' version: '4.013' - class: Pod::Weaver::Section::Region name: '@Author::PERLANCAR/postlude' version: '4.013' - class: Pod::Weaver::Section::Completion::GetoptLongComplete name: '@Author::PERLANCAR/Completion::GetoptLongComplete' version: '0.08' - class: Pod::Weaver::Section::Completion::GetoptLongSubcommand name: '@Author::PERLANCAR/Completion::GetoptLongSubcommand' version: '0.04' - class: Pod::Weaver::Section::Completion::GetoptLongMore name: '@Author::PERLANCAR/Completion::GetoptLongMore' version: '0.001' - class: Pod::Weaver::Section::Homepage::DefaultCPAN name: '@Author::PERLANCAR/Homepage::DefaultCPAN' version: '0.05' - class: Pod::Weaver::Section::Source::DefaultGitHub name: '@Author::PERLANCAR/Source::DefaultGitHub' version: '0.07' - class: Pod::Weaver::Section::Bugs::DefaultRT name: '@Author::PERLANCAR/Bugs::DefaultRT' version: '0.06' - class: Pod::Weaver::Section::Authors name: '@Author::PERLANCAR/Authors' version: '4.013' - class: Pod::Weaver::Section::Legal name: '@Author::PERLANCAR/Legal' version: '4.013' - class: Pod::Weaver::Plugin::Rinci name: '@Author::PERLANCAR/Rinci' version: '0.77' - class: Pod::Weaver::Plugin::AppendPrepend name: '@Author::PERLANCAR/AppendPrepend' version: '0.01' - class: Pod::Weaver::Plugin::EnsureUniqueSections name: '@Author::PERLANCAR/EnsureUniqueSections' version: '0.121550' - class: Pod::Weaver::Plugin::SingleEncoding name: '@Author::PERLANCAR/SingleEncoding' version: '4.013' - class: Pod::Weaver::Plugin::PERLANCAR::SortSections name: '@Author::PERLANCAR/PERLANCAR::SortSections' version: '0.06' name: '@Author::PERLANCAR/PodWeaver' version: '4.008' - class: Dist::Zilla::Plugin::PruneFiles name: '@Author::PERLANCAR/PruneFiles' version: '6.008' - class: Dist::Zilla::Plugin::Pod2Readme name: '@Author::PERLANCAR/Pod2Readme' version: '0.004' - class: Dist::Zilla::Plugin::Rinci::AddPrereqs name: '@Author::PERLANCAR/Rinci::AddPrereqs' version: '0.13' - class: Dist::Zilla::Plugin::Rinci::AddToDb name: '@Author::PERLANCAR/Rinci::AddToDb' version: '0.01' - class: Dist::Zilla::Plugin::Rinci::Validate name: '@Author::PERLANCAR/Rinci::Validate' version: '0.24' - class: Dist::Zilla::Plugin::SetScriptShebang name: '@Author::PERLANCAR/SetScriptShebang' version: '0.01' - class: Dist::Zilla::Plugin::Test::Compile config: Dist::Zilla::Plugin::Test::Compile: bail_out_on_fail: '0' fail_on_warning: author fake_home: 0 filename: t/00-compile.t module_finder: - ':InstallModules' needs_display: 0 phase: test script_finder: - ':PerlExecFiles' skips: [] name: '@Author::PERLANCAR/Test::Compile' version: '2.054' - class: Dist::Zilla::Plugin::Test::Perl::Critic name: '@Author::PERLANCAR/Test::Perl::Critic' version: '3.001' - class: Dist::Zilla::Plugin::Test::Rinci name: '@Author::PERLANCAR/Test::Rinci' version: '0.03' - class: Dist::Zilla::Plugin::StaticInstall config: Dist::Zilla::Plugin::StaticInstall: dry_run: 0 mode: on name: '@Author::PERLANCAR/StaticInstall' version: '0.010' - class: Dist::Zilla::Plugin::EnsureSQLSchemaVersionedTest name: '@Author::PERLANCAR/EnsureSQLSchemaVersionedTest' version: '0.03' - class: Dist::Zilla::Plugin::Acme::CPANLists::Blacklist name: '@Author::PERLANCAR/Acme::CPANLists::Blacklist' version: '0.04' - class: Dist::Zilla::Plugin::Prereqs::EnsureVersion name: '@Author::PERLANCAR/Prereqs::EnsureVersion' version: '0.04' - class: Dist::Zilla::Plugin::Prereqs::CheckCircular name: '@Author::PERLANCAR/Prereqs::CheckCircular' version: '0.005' - class: Dist::Zilla::Plugin::UploadToCPAN::WWWPAUSESimple name: '@Author::PERLANCAR/UploadToCPAN::WWWPAUSESimple' version: '0.04' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: runtime type: requires name: Prereqs version: '6.008' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: x_spec name: DevelopX_spec version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':TestFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':ExtraTestFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':PerlExecFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':AllFiles' version: '6.008' - class: Dist::Zilla::Plugin::FinderCode name: ':NoFiles' version: '6.008' zilla: class: Dist::Zilla::Dist::Builder config: is_trial: '0' version: '6.008' x_authority: cpan:PERLANCAR x_serialization_backend: 'YAML::Tiny version 1.69' x_static_install: 1 Rinci-1.1.86/t/0000775000175000017500000000000013212744366010564 5ustar u1u1Rinci-1.1.86/t/author-critic.t0000644000175000017500000000040313212744366013521 0ustar u1u1#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } use strict; use warnings; use Test::Perl::Critic (-profile => "perlcritic.rc") x!! -e "perlcritic.rc"; all_critic_ok(); Rinci-1.1.86/t/author-pod-syntax.t0000644000175000017500000000045413212744366014360 0ustar u1u1#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Rinci-1.1.86/t/00-compile.t0000644000175000017500000000232213212744366012613 0ustar u1u1use 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.054 use Test::More; plan tests => 1 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Rinci.pm' ); # no fake home requested my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib'; use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING}; Rinci-1.1.86/t/author-pod-coverage.t0000644000175000017500000000053613212744366014626 0ustar u1u1#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); Rinci-1.1.86/weaver.ini0000644000175000017500000000002513212744366012306 0ustar u1u1[@Author::PERLANCAR] Rinci-1.1.86/META.json0000644000175000017500000004600613212744366011746 0ustar u1u1{ "abstract" : "Language-neutral metadata for your code", "author" : [ "perlancar " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.008, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Rinci", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" }, "x_spec" : { "DefHash" : "v1.0.6", "Sah" : "v0.9.36" } }, "test" : { "requires" : { "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Rinci" }, "homepage" : "https://metacpan.org/release/Rinci", "repository" : { "type" : "git", "url" : "git://github.com/perlancar/perl-Rinci.git", "web" : "https://github.com/perlancar/perl-Rinci" } }, "version" : "1.1.86", "x_Dist_Zilla" : { "perl" : { "version" : "5.024000" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [], "exclude_match" : [], "follow_symlinks" : 0, "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." } }, "name" : "@Author::PERLANCAR/@Filter/GatherDir", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "@Author::PERLANCAR/@Filter/PruneCruft", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ManifestSkip", "name" : "@Author::PERLANCAR/@Filter/ManifestSkip", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Author::PERLANCAR/@Filter/MetaYAML", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Author::PERLANCAR/@Filter/License", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "@Author::PERLANCAR/@Filter/PodCoverageTests", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Author::PERLANCAR/@Filter/PodSyntaxTests", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ExtraTests", "name" : "@Author::PERLANCAR/@Filter/ExtraTests", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::PERLANCAR/@Filter/ExecDir", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ShareDir", "name" : "@Author::PERLANCAR/@Filter/ShareDir", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::MakeMaker", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "@Author::PERLANCAR/@Filter/MakeMaker", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Author::PERLANCAR/@Filter/Manifest", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Author::PERLANCAR/@Filter/ConfirmRelease", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::PERLANCAR/ExecDir script", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::PERLANCAR::BeforeBuild", "name" : "@Author::PERLANCAR/PERLANCAR::BeforeBuild", "version" : "0.590" }, { "class" : "Dist::Zilla::Plugin::Rinci::AbstractFromMeta", "name" : "@Author::PERLANCAR/Rinci::AbstractFromMeta", "version" : "0.10" }, { "class" : "Dist::Zilla::Plugin::PodnameFromFilename", "name" : "@Author::PERLANCAR/PodnameFromFilename", "version" : "0.02" }, { "class" : "Dist::Zilla::Plugin::PERLANCAR::EnsurePrereqToSpec", "name" : "@Author::PERLANCAR/PERLANCAR::EnsurePrereqToSpec", "version" : "0.05" }, { "class" : "Dist::Zilla::Plugin::PERLANCAR::MetaResources", "name" : "@Author::PERLANCAR/PERLANCAR::MetaResources", "version" : "0.03" }, { "class" : "Dist::Zilla::Plugin::CheckChangeLog", "name" : "@Author::PERLANCAR/CheckChangeLog", "version" : "0.04" }, { "class" : "Dist::Zilla::Plugin::CheckMetaResources", "name" : "@Author::PERLANCAR/CheckMetaResources", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::CopyrightYearFromGit", "name" : "@Author::PERLANCAR/CopyrightYearFromGit", "version" : "0.003" }, { "class" : "Dist::Zilla::Plugin::IfBuilt", "name" : "@Author::PERLANCAR/IfBuilt", "version" : "0.03" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Author::PERLANCAR/MetaJSON", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Author::PERLANCAR/MetaConfig", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::Authority", "name" : "@Author::PERLANCAR/Authority", "version" : "1.009" }, { "class" : "Dist::Zilla::Plugin::OurDate", "name" : "@Author::PERLANCAR/OurDate", "version" : "0.03" }, { "class" : "Dist::Zilla::Plugin::OurDist", "name" : "@Author::PERLANCAR/OurDist", "version" : "0.02" }, { "class" : "Dist::Zilla::Plugin::PERLANCAR::OurPkgVersion", "name" : "@Author::PERLANCAR/PERLANCAR::OurPkgVersion", "version" : "0.04" }, { "class" : "Dist::Zilla::Plugin::PodWeaver", "config" : { "Dist::Zilla::Plugin::PodWeaver" : { "finder" : [ ":InstallModules", ":ExecFiles" ], "plugins" : [ { "class" : "Pod::Weaver::Plugin::EnsurePod5", "name" : "@CorePrep/EnsurePod5", "version" : "4.013" }, { "class" : "Pod::Weaver::Plugin::H1Nester", "name" : "@CorePrep/H1Nester", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Name", "name" : "@Author::PERLANCAR/Name", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Version", "name" : "@Author::PERLANCAR/Version", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::PERLANCAR/prelude", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "SYNOPSIS", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "DESCRIPTION", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "OVERVIEW", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "ATTRIBUTES", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "METHODS", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "FUNCTIONS", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Leftovers", "name" : "@Author::PERLANCAR/Leftovers", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::PERLANCAR/postlude", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Completion::GetoptLongComplete", "name" : "@Author::PERLANCAR/Completion::GetoptLongComplete", "version" : "0.08" }, { "class" : "Pod::Weaver::Section::Completion::GetoptLongSubcommand", "name" : "@Author::PERLANCAR/Completion::GetoptLongSubcommand", "version" : "0.04" }, { "class" : "Pod::Weaver::Section::Completion::GetoptLongMore", "name" : "@Author::PERLANCAR/Completion::GetoptLongMore", "version" : "0.001" }, { "class" : "Pod::Weaver::Section::Homepage::DefaultCPAN", "name" : "@Author::PERLANCAR/Homepage::DefaultCPAN", "version" : "0.05" }, { "class" : "Pod::Weaver::Section::Source::DefaultGitHub", "name" : "@Author::PERLANCAR/Source::DefaultGitHub", "version" : "0.07" }, { "class" : "Pod::Weaver::Section::Bugs::DefaultRT", "name" : "@Author::PERLANCAR/Bugs::DefaultRT", "version" : "0.06" }, { "class" : "Pod::Weaver::Section::Authors", "name" : "@Author::PERLANCAR/Authors", "version" : "4.013" }, { "class" : "Pod::Weaver::Section::Legal", "name" : "@Author::PERLANCAR/Legal", "version" : "4.013" }, { "class" : "Pod::Weaver::Plugin::Rinci", "name" : "@Author::PERLANCAR/Rinci", "version" : "0.77" }, { "class" : "Pod::Weaver::Plugin::AppendPrepend", "name" : "@Author::PERLANCAR/AppendPrepend", "version" : "0.01" }, { "class" : "Pod::Weaver::Plugin::EnsureUniqueSections", "name" : "@Author::PERLANCAR/EnsureUniqueSections", "version" : "0.121550" }, { "class" : "Pod::Weaver::Plugin::SingleEncoding", "name" : "@Author::PERLANCAR/SingleEncoding", "version" : "4.013" }, { "class" : "Pod::Weaver::Plugin::PERLANCAR::SortSections", "name" : "@Author::PERLANCAR/PERLANCAR::SortSections", "version" : "0.06" } ] } }, "name" : "@Author::PERLANCAR/PodWeaver", "version" : "4.008" }, { "class" : "Dist::Zilla::Plugin::PruneFiles", "name" : "@Author::PERLANCAR/PruneFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::Pod2Readme", "name" : "@Author::PERLANCAR/Pod2Readme", "version" : "0.004" }, { "class" : "Dist::Zilla::Plugin::Rinci::AddPrereqs", "name" : "@Author::PERLANCAR/Rinci::AddPrereqs", "version" : "0.13" }, { "class" : "Dist::Zilla::Plugin::Rinci::AddToDb", "name" : "@Author::PERLANCAR/Rinci::AddToDb", "version" : "0.01" }, { "class" : "Dist::Zilla::Plugin::Rinci::Validate", "name" : "@Author::PERLANCAR/Rinci::Validate", "version" : "0.24" }, { "class" : "Dist::Zilla::Plugin::SetScriptShebang", "name" : "@Author::PERLANCAR/SetScriptShebang", "version" : "0.01" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : 0, "fail_on_warning" : "author", "fake_home" : 0, "filename" : "t/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "test", "script_finder" : [ ":PerlExecFiles" ], "skips" : [] } }, "name" : "@Author::PERLANCAR/Test::Compile", "version" : "2.054" }, { "class" : "Dist::Zilla::Plugin::Test::Perl::Critic", "name" : "@Author::PERLANCAR/Test::Perl::Critic", "version" : "3.001" }, { "class" : "Dist::Zilla::Plugin::Test::Rinci", "name" : "@Author::PERLANCAR/Test::Rinci", "version" : "0.03" }, { "class" : "Dist::Zilla::Plugin::StaticInstall", "config" : { "Dist::Zilla::Plugin::StaticInstall" : { "dry_run" : 0, "mode" : "on" } }, "name" : "@Author::PERLANCAR/StaticInstall", "version" : "0.010" }, { "class" : "Dist::Zilla::Plugin::EnsureSQLSchemaVersionedTest", "name" : "@Author::PERLANCAR/EnsureSQLSchemaVersionedTest", "version" : "0.03" }, { "class" : "Dist::Zilla::Plugin::Acme::CPANLists::Blacklist", "name" : "@Author::PERLANCAR/Acme::CPANLists::Blacklist", "version" : "0.04" }, { "class" : "Dist::Zilla::Plugin::Prereqs::EnsureVersion", "name" : "@Author::PERLANCAR/Prereqs::EnsureVersion", "version" : "0.04" }, { "class" : "Dist::Zilla::Plugin::Prereqs::CheckCircular", "name" : "@Author::PERLANCAR/Prereqs::CheckCircular", "version" : "0.005" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN::WWWPAUSESimple", "name" : "@Author::PERLANCAR/UploadToCPAN::WWWPAUSESimple", "version" : "0.04" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "runtime", "type" : "requires" } }, "name" : "Prereqs", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "x_spec" } }, "name" : "DevelopX_spec", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.008" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.008" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.008" } }, "x_authority" : "cpan:PERLANCAR", "x_serialization_backend" : "Cpanel::JSON::XS version 3.0217", "x_static_install" : 1 } Rinci-1.1.86/MANIFEST0000644000175000017500000000067713212744366011462 0ustar u1u1# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.008. Changes LICENSE MANIFEST META.json META.yml Makefile.PL README dist.ini lib/Rinci.pm lib/Rinci.pod lib/Rinci/FAQ.pod lib/Rinci/Transaction.pod lib/Rinci/Undo.pod lib/Rinci/Upgrading.pod lib/Rinci/function.pod lib/Rinci/package.pod lib/Rinci/resmeta.pod lib/Rinci/variable.pod t/00-compile.t t/author-critic.t t/author-pod-coverage.t t/author-pod-syntax.t weaver.ini Rinci-1.1.86/lib/0000775000175000017500000000000013212744366011067 5ustar u1u1Rinci-1.1.86/lib/Rinci.pod0000644000175000017500000004477113212744366012652 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 SPECIFICATION VERSION 1.1 =head1 VERSION This document describes version 1.1.86 of Rinci (from Perl distribution Rinci), released on 2017-12-09. =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, L More convenient replacements/wrappers for L or L if your functions are equipped with Rinci metadata. Automatically provide export tags (using information in the C property). Automatically wrap functions using Perinci::Sub::Wrapper when exporting (Perinci::Exporter). =item * Perinci::To::* modules Convert metadata to various other documents, for example L to generate documentation. =item * L (several flavors: L, L, 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 C. 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 C 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 * url => 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 * caption => STR From DefHash. 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", caption => "Article describing foo using Bar algorithm", }, { url => "../Bar2/", caption => "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 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 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 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 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 AUTHOR perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/0000775000175000017500000000000013212744366012133 5ustar u1u1Rinci-1.1.86/lib/Rinci/package.pod0000644000175000017500000000316013212744366014230 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 SPECIFICATION VERSION 1.1 =head1 VERSION This document describes version 1.1.86 of Rinci::package (from Perl distribution Rinci), released on 2017-12-09. =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 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 SEE ALSO L =head1 AUTHOR perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/Upgrading.pod0000644000175000017500000000703113212744366014556 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 This document describes version 1.1.86 of Rinci::Upgrading (from Perl distribution Rinci), released on 2017-12-09. =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 perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/Undo.pod0000644000175000017500000002212213212744366013541 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 SPECIFICATION VERSION 1.1 =head1 VERSION This document describes version 1.1.86 of Rinci::Undo (from Perl distribution Rinci), released on 2017-12-09. =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 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 SEE ALSO Related specifications: L =head1 AUTHOR perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/Transaction.pod0000644000175000017500000007152013212744366015127 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 SPECIFICATION VERSION Rinci 1.1, protocol version 2 =head1 VERSION This document describes version 1.1.86 of Rinci::Transaction (from Perl distribution Rinci), released on 2017-12-09. =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 to get undo information, and second to actually perform 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 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 SEE ALSO Transaction behavior is largely based on PostgreSQL. Related specifications: L, L Implementations: L =head1 AUTHOR perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/FAQ.pod0000644000175000017500000001415513212744366013252 0ustar u1u1package Rinci::FAQ; # just to make PodWeaver happy # DATE # VERSION 1; # ABSTRACT: Metadata for your functions/methods __END__ =pod =encoding UTF-8 =head1 NAME Rinci::FAQ - Metadata for your functions/methods =head1 VERSION This document describes version 1.1.86 of Rinci::FAQ (from Perl distribution Rinci), released on 2017-12-09. =head1 FAQ =head2 Rinci::function =over =item * Why do you make enveloped result an arary instead of hash? For example, a hash-based enveloped result can be something like: {status=>200, message=>"OK", result=>42, meta1=>..., meta2=>...} This has the benefit of a single container, but I picked array because of the brevity for simple cases (which are the majority), e.g.: [200] # versus {status=>200} [200, "OK"] # versus {status=>200, message=>"OK"} When handling enveloped result, the array version is also shorter: if ($res->[0] == 200) { ... } # versus: if ($res->{status} == 200) { ... } print "Error $res->[0] - $res->[1]"; # versus: print "Error $res->{status} - $res->{message}"; The hash version is more obvious for first-time reader, but after just some amount of time, C<< $res->[0] >>, C<< $res->[1] >> will become obvious if you use it consistently. As a bonus, arrays are faster and more space-efficient than hashes. =item * How do you indicate transient/temporary vs permanent errors? Some protocols, like SMTP or POP, defines 4xx codes as temporary errors and 5xx as permanent ones. This gives clue to clients whether to retry or not. HTTP, which Rinci is modelled after, does not provide such distinction to its status codes. However, Rinci defines a C result metadata that can be used for such purpose, e.g.: [500, "Can't submit mail, we are being blocked by RBL", undef, {perm_err=>0}] [500, "Can't submit mail, destination address does not exist", undef, {perm_err=>1}] =item * How to handle binary data? To accept binary data, you can set one or more arguments as having the schema type C (instead of C): args => { data => { schema => 'buf*', req=>1 }, } To return binary data, you can set result's schema type to C, e.g.: result => { schema => 'buf*' } For handling binary data when writing Perl-based command-line applications, see L. =item * How to accept partial data? First, set an argument property C to true to signify that this argument accept partial value. You can then call with special arguments C<-arg_len>, C<-arg_part_start>, C<-arg_part_len>. See L for more details. L can also do this via HTTP Content-Range. =item * How to accept streaming input? Many program environments (like in Unix) have the concept of standard input. Rinci provides a thin abstraction over this. You can set the argument property C to true. This way, in most implementation like in Perl, your function will receive the argument value as filehandle which it can then read from. See C property in C function metadata property in L for an example. Your function can also read from standard input directly, but this means you cannot use conveniences like the C, where the command-line framework can supply an argument value for you from various sources including standard input and/or files. =item * How to produce streaming output? Many program environments (like in Unix) have the concept of standard output. To produce output stream, you can set result metadata property C to true. And then in the result you can put a filehandle or an object that responds to getline/getitem methods. =item * What is the difference between accepting partial data and streaming input? If a function accepts partial data, to send a large data without taking up too much memory, a caller needs to break the data into several parts and call the function several times, each with a different part. If a function accepts a stream input, to send a large data a caller can send a filehandle/iterator object from which the function can read the data iteratively. Stream input is easier and simpler for the function writer to write. A caller also only needs to call the function once instead of multiple times. However, there is no resume capability. On the other hand, partial input data is easier to implement with Riap::HTTP, as it maps rather closely to HTTP Content-Range. If you are uploading a large data over a network to a function, partial input data is preferred because of its ease to work with HTTP and its resume ability. However, if input is really a stream (i.e. unknown/infinite length), then streaming input is the option to use. =item * What is the difference between returning partial result and streaming output? If a function can return partial result, to retrieve a large result from a function a caller can calls several times and each time request to retrieve parts of result. If a function returns output stream, a caller can then retrieve data from the stream iteratively. Output stream is easier to handle by the caller. The caller also only needs to call once instead of multiple times. However, there is no resume capability. On the other hand, partial result is easier to implement with Riap::HTTP, as it maps rather closely to HTTP Content-Range. If you are retrieving a large data over a network from a function, partial result is preferred because of its ease to work with HTTP =back =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 perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/variable.pod0000644000175000017500000000344613212744366014431 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 SPECIFICATION VERSION 1.1 =head1 VERSION This document describes version 1.1.86 of Rinci::variable (from Perl distribution Rinci), released on 2017-12-09. =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 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 SEE ALSO L =head1 AUTHOR perlancar =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012 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. =cut Rinci-1.1.86/lib/Rinci/resmeta.pod0000644000175000017500000001377513212744366014312 0ustar u1u1package Rinci::resmeta; # just to make PodWeaver happy # DATE # VERSION 1; # ABSTRACT: Function/method result metadata __END__ =pod =encoding UTF-8 =head1 NAME Rinci::resmeta - Function/method result metadata =head1 SPECIFICATION VERSION 1.1 =head1 VERSION This document describes version 1.1.86 of Rinci::resmeta (from Perl distribution Rinci), released on 2017-12-09. =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 Property: perm_err => bool Indicate that error is permanent (instead of temporary/transient). This is to provide a feature like that found in SMTP/POP protocol, where 4xx codes indicate transient errors and 5xx permanent ones. =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