Imported Upstream version 0.40 upstream/0.40
authorTizenOpenSource <tizenopensrc@samsung.com>
Wed, 14 Feb 2024 04:45:22 +0000 (13:45 +0900)
committerTizenOpenSource <tizenopensrc@samsung.com>
Wed, 14 Feb 2024 04:45:22 +0000 (13:45 +0900)
60 files changed:
CONTRIBUTING [new file with mode: 0644]
Changes [new file with mode: 0644]
INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
META.json [new file with mode: 0644]
META.yml [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
bin/package-stash-conflicts [new file with mode: 0644]
dist.ini [new file with mode: 0644]
inc/ExtUtils/HasCompiler.pm [new file with mode: 0644]
lib/Package/Stash.pm [new file with mode: 0644]
lib/Package/Stash/Conflicts.pm [new file with mode: 0644]
lib/Package/Stash/PP.pm [new file with mode: 0644]
t/00-report-prereqs.dd [new file with mode: 0644]
t/00-report-prereqs.t [new file with mode: 0644]
t/addsub.t [new file with mode: 0644]
t/anon-basic.t [new file with mode: 0644]
t/anon.t [new file with mode: 0644]
t/bare-anon-basic.t [new file with mode: 0644]
t/bare-anon.t [new file with mode: 0644]
t/basic.t [new file with mode: 0644]
t/compile-time.t [new file with mode: 0644]
t/edge-cases.t [new file with mode: 0644]
t/extension.t [new file with mode: 0644]
t/get.t [new file with mode: 0644]
t/impl-selection/basic-pp.t [new file with mode: 0644]
t/impl-selection/basic-xs.t [new file with mode: 0644]
t/impl-selection/bug-rt-78272.t [new file with mode: 0644]
t/impl-selection/choice.t [new file with mode: 0644]
t/impl-selection/env.t [new file with mode: 0644]
t/impl-selection/var.t [new file with mode: 0644]
t/io.t [new file with mode: 0644]
t/isa.t [new file with mode: 0644]
t/lib/CompileTime.pm [new file with mode: 0644]
t/lib/Package/Stash.pm [new file with mode: 0644]
t/magic.t [new file with mode: 0644]
t/paamayim_nekdotayim.t [new file with mode: 0644]
t/scalar-values.t [new file with mode: 0644]
t/stash-deletion.t [new file with mode: 0644]
t/synopsis.t [new file with mode: 0644]
t/warnings-taint.t [new file with mode: 0644]
t/warnings.t [new file with mode: 0644]
t/zzz-check-breaks.t [new file with mode: 0644]
xt/author/00-compile.t [new file with mode: 0644]
xt/author/distmeta.t [new file with mode: 0644]
xt/author/eol.t [new file with mode: 0644]
xt/author/kwalitee.t [new file with mode: 0644]
xt/author/leaks-debug.t [new file with mode: 0644]
xt/author/leaks.t [new file with mode: 0644]
xt/author/minimum-version.t [new file with mode: 0644]
xt/author/mojibake.t [new file with mode: 0644]
xt/author/no-tabs.t [new file with mode: 0644]
xt/author/pod-no404s.t [new file with mode: 0644]
xt/author/pod-spell.t [new file with mode: 0644]
xt/author/pod-syntax.t [new file with mode: 0644]
xt/author/portability.t [new file with mode: 0644]
xt/release/changes_has_content.t [new file with mode: 0644]
xt/release/cpan-changes.t [new file with mode: 0644]

diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644 (file)
index 0000000..d2b713d
--- /dev/null
@@ -0,0 +1,105 @@
+
+CONTRIBUTING
+
+Thank you for considering contributing to this distribution.  This file
+contains instructions that will help you work with the source code.
+
+PLEASE NOTE that if you have any questions or difficulties, you can reach the
+maintainer(s) through the bug queue described later in this document
+(preferred), or by emailing the releaser directly. You are not required to
+follow any of the steps in this document to submit a patch or bug report;
+these are just recommendations, intended to help you (and help us help you
+faster).
+
+The distribution is managed with Dist::Zilla (https://metacpan.org/release/Dist-Zilla).
+This means than many of the usual files you might expect are not in the
+repository, but are generated at release time (e.g. Makefile.PL).
+
+However, you can run tests directly using the 'prove' tool:
+
+  $ prove -l
+  $ prove -lv t/some_test_file.t
+  $ prove -lvr t/
+
+In most cases, 'prove' is entirely sufficient for you to test any patches you
+have.
+
+You may need to satisfy some dependencies.  The easiest way to satisfy
+dependencies is to install the last release -- this is available at
+https://metacpan.org/release/Package-Stash
+
+If you use cpanminus, you can do it without downloading the tarball first:
+
+  $ cpanm --reinstall --installdeps --with-recommends Package::Stash
+
+Dist::Zilla is a very powerful authoring tool, but requires a number of
+author-specific plugins.  If you would like to use it for contributing,
+install it from CPAN, then run one of the following commands, depending on
+your CPAN client:
+
+  $ cpan `dzil authordeps --missing`
+or
+  $ dzil authordeps --missing | cpanm
+
+You should then also install any additional requirements not needed by the
+dzil build but may be needed by tests or other development:
+
+  $ cpan `dzil listdeps --author --missing`
+or
+  $ dzil listdeps --author --missing | cpanm
+
+Or, you can use the 'dzil stale' command to install all requirements at once:
+
+  $ cpan Dist::Zilla::App::Command::stale
+  $ cpan `dzil stale --all`
+or
+  $ cpanm Dist::Zilla::App::Command::stale
+  $ dzil stale --all | cpanm
+
+You can also do this via cpanm directly:
+
+  $ cpanm --reinstall --installdeps --with-develop --with-recommends Package::Stash
+
+Once installed, here are some dzil commands you might try:
+
+  $ dzil build
+  $ dzil test
+  $ dzil test --release
+  $ dzil xtest
+  $ dzil listdeps --json
+  $ dzil build --notgz
+
+You can learn more about Dist::Zilla at http://dzil.org/.
+
+The code for this distribution is hosted at GitHub. The repository is:
+
+https://github.com/moose/Package-Stash
+
+You can submit code changes by forking the repository, pushing your code
+changes to your clone, and then submitting a pull request. Please include a
+suitable end-user-oriented entry in the Changes file describing your change.
+Detailed instructions for doing that is available here:
+
+https://help.github.com/articles/creating-a-pull-request
+
+All pull requests for this distribution will be automatically tested on Linux
+by Travis at: https://travis-ci.com/moose/Package-Stash
+Results will be visible in the pull request on GitHub. Follow the appropriate
+links for details when tests fail. Changes will not be mergeable until all
+tests pass.
+
+If you have found a bug, but do not have an accompanying patch to fix it, you
+can submit an issue report here:
+https://rt.cpan.org/Public/Dist/Display.html?Name=Package-Stash
+or via email: bug-Package-Stash@rt.cpan.org
+This is a good place to send your questions about the usage of this distribution.
+
+If you send me a patch or pull request, your name and email address will be
+included in the documentation as a contributor (using the attribution on the
+commit or patch), unless you specifically request for it not to be.  If you
+wish to be listed under a different name or address, you should submit a pull
+request to the .mailmap file to contain the correct mapping.
+
+
+This file was generated via Dist::Zilla::Plugin::GenerateFile::FromShareDir 0.015
+from a template file originating in Dist-Zilla-PluginBundle-Author-ETHER-0.162.
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..85f6c8a
--- /dev/null
+++ b/Changes
@@ -0,0 +1,154 @@
+Revision history for Package-Stash
+
+0.40      2022-02-21 05:38:38Z
+     - update bundled version of ExtUtils::HasCompiler, for new new Apple
+       include paths
+
+0.39      2020-11-22 01:26:43Z
+     - updated distribution metadata and packaging
+     - configuration phase now uses ExtUtils::HasCompiler
+
+0.38      2018-12-31 03:25:07Z
+     - avoid new warning in 5.21.x regarding too many arguments for
+       printf/sprintf (Renée Bäcker)
+     - canonical repository moved to https://github.com/moose/Package-Stash
+
+0.37  2014-09-21
+      - fix spurious warning in taint mode (Niko Tyni, #12)
+
+0.36  2013-09-03
+      - support building with PUREPERL_ONLY
+
+0.35  2013-07-09
+      - remove old deprecated api
+
+0.34  2013-01-04
+      - support anonymous stashes on newer perl versions
+      - prevent harmful effects from invalid settings for
+        $ENV{PACKAGE_STASH_IMPLEMENTATION}
+      - switch to Module::Implementation
+
+0.33  2011-09-28
+      - add conflict on MooseX::Method::Signatures 0.36 (ether)
+
+0.32  2011-09-05
+      - bring the behavior of has_symbol for nonexistant scalars into line with
+        the xs version
+      - invalid package names (for instance, Foo:Bar) are not allowed
+      - invalid stash entry names (anything containing ::) are not allowed
+
+0.31  2011-08-08
+      - fix ->add_symbol('$foo', qr/sdlfk/) on 5.12+
+      - fix ->add_symbol('$foo', \v1.2.3) on 5.10+
+
+0.30  2011-07-21
+      - fix compiler detection in Makefile.PL (ribasushi)
+
+0.29  2011-04-06
+      - no, *really* skip the package-stash-conflict script
+
+0.28  2011-03-29
+      - META.json fixes (mst)
+
+0.27  2011-03-27
+      - also skip the package-stash-conflicts script (Father Chrysostomos)
+
+0.26  2011-03-04
+      - make the namespace cache lazy and weak, in case the stash is deleted
+      - but, this doesn't work on 5.8, so disable the namespace caching
+        entirely there
+
+0.25  2011-01-25
+      - make the leak tests author-only, since some smokers run release tests
+
+0.24  2011-01-17
+      - oops, i did need the Test::Requires dep
+
+0.23  2011-01-11
+      - lower perl prereq to 5.8.1 (ribasushi)
+      - make the leak tests release-only, since they keep randomly failing on
+        platforms i don't have access to. people are encouraged to submit
+        patches for these if they are affected.
+
+0.22  2011-01-05
+      - bump ::XS dep again
+
+0.21  2011-01-05
+      - bump Package::Stash::XS dep, since a bug was fixed there
+
+0.20  2011-01-03
+      - one more fix for the Conflicts plugin
+
+0.19  2011-01-03
+      - stop manually inserting conflict checking into Makefile.PL since we're
+        using the dzil Conflicts plugin now
+
+0.18  2011-01-03
+      - non-trial release
+
+0.17-TRIAL  2011-01-03
+      - use Dist::Zilla::Plugin::Conflicts rather than doing it by hand
+      - silence deprecation warnings for the method renaming for now
+
+0.16-TRIAL  2010-12-31
+      - use Dist::CheckConflicts
+
+0.15-TRIAL  2010-11-16
+      - split the XS conversion out to its own dist (Package-Stash-XS), and
+        convert Package::Stash into a module which loads either the XS or pure
+        perl implementation, depending on what's available
+
+0.14-TRIAL  2010-11-14
+      - complete rewrite in C, for speed (this includes the vivification
+        changes from earlier). should be entirely backwards compatible
+        otherwise (in terms of documented api anyway).
+
+      - methods were renamed for brevity: s/_package//
+
+0.13  2010-10-31
+      - revert the vivification changes for now, to get an actual release out
+        with Test::Fatal
+
+0.12-TRIAL  2010-10-27
+      - actually include the conflict stuff in the release (bah)
+
+0.11-TRIAL  2010-10-27
+      - conflict on mx-role-withoverloading too
+
+0.10-TRIAL  2010-10-27
+      - only do the weird ISA special-casing on perl versions where it's broken
+
+0.09-TRIAL  2010-10-27
+      - clean up the vivication code a lot, make it behave more sanely
+      - use Test::Fatal instead of Test::Exception (Justin Hunter)
+
+0.08  2010-09-18
+      - oops, accidentally included some experimental changes in that last
+        release, that break things
+
+0.07  2010-09-18
+      - non-dev release
+
+0.06-TRIAL  2010-08-26
+      - re-enable the caching of the stash, since I can't reproduce the bug
+        at all
+
+0.05  2010-06-15
+      - bump Test::More requirement for done_testing
+
+      - update packaging stuff
+
+0.04  2010-06-13
+      - get_package_symbol now doesn't autovivify stash entries. A new method
+        get_or_add_package_symbol can now be used for that behavior.
+
+      - Update %DB::sub on add_package_symbol (Tim Bunce).
+
+0.03  2010-05-14
+      - Rename from Stash::Manip to Package::Stash
+
+0.02  2010-05-13
+      - Need to dep on Test::Exception
+
+0.01  2010-05-12
+      - Initial release
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..32d967f
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,75 @@
+This is the Perl distribution Package-Stash.
+
+Installing Package-Stash is straightforward.
+
+## Installation with cpanm
+
+If you have cpanm, you only need one line:
+
+    % cpanm Package::Stash
+
+If it does not have permission to install modules to the current perl, cpanm
+will automatically set up and install to a local::lib in your home directory.
+See the local::lib documentation (https://metacpan.org/pod/local::lib) for
+details on enabling it in your environment.
+
+## Installing with the CPAN shell
+
+Alternatively, if your CPAN shell is set up, you should just be able to do:
+
+    % cpan Package::Stash
+
+## Manual installation
+
+As a last resort, you can manually install it. If you have not already
+downloaded the release tarball, you can find the download link on the module's
+MetaCPAN page: https://metacpan.org/pod/Package::Stash
+
+Untar the tarball, install configure prerequisites (see below), then build it:
+
+    % perl Makefile.PL
+    % make && make test
+
+Then install it:
+
+    % make install
+
+On Windows platforms, you should use `dmake` or `nmake`, instead of `make`.
+
+If your perl is system-managed, you can create a local::lib in your home
+directory to install modules to. For details, see the local::lib documentation:
+https://metacpan.org/pod/local::lib
+
+The prerequisites of this distribution will also have to be installed manually. The
+prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated
+by running the manual build process described above.
+
+## Configure Prerequisites
+
+This distribution requires other modules to be installed before this
+distribution's installer can be run.  They can be found under the
+"configure_requires" key of META.yml or the
+"{prereqs}{configure}{requires}" key of META.json.
+
+## Other Prerequisites
+
+This distribution may require additional modules to be installed after running
+Makefile.PL.
+Look for prerequisites in the following phases:
+
+* to run make, PHASE = build
+* to use the module code itself, PHASE = runtime
+* to run tests, PHASE = test
+
+They can all be found in the "PHASE_requires" key of MYMETA.yml or the
+"{prereqs}{PHASE}{requires}" key of MYMETA.json.
+
+## Documentation
+
+Package-Stash documentation is available as POD.
+You can run `perldoc` from a shell to read the documentation:
+
+    % perldoc Package::Stash
+
+For more information on installing Perl modules via CPAN, please see:
+https://www.cpan.org/modules/INSTALL.html
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..3f579ba
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,379 @@
+This software is copyright (c) 2022 by Jesse Luehrs.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+Terms of the Perl programming language system itself
+
+a) the GNU General Public License as published by the Free
+   Software Foundation; either version 1, or (at your option) any
+   later version, or
+b) the "Artistic License"
+
+--- The GNU General Public License, Version 1, February 1989 ---
+
+This software is Copyright (c) 2022 by Jesse Luehrs.
+
+This is free software, licensed under:
+
+  The GNU General Public License, Version 1, February 1989
+
+                    GNU GENERAL PUBLIC LICENSE
+                     Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The license agreements of most software companies try to keep users
+at the mercy of those companies.  By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must tell them their rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License.  The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications.  Each
+licensee is addressed as "you".
+
+  1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program.  You may charge a fee for the physical act of
+transferring a copy.
+
+  2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating that
+    you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish, that
+    in whole or in part contains the Program or any part thereof, either
+    with or without modifications, to be licensed at no charge to all
+    third parties under the terms of this General Public License (except
+    that you may choose to grant warranty protection to some or all
+    third parties, at your option).
+
+    c) If the modified program normally reads commands interactively when
+    run, you must cause it, when started running for such interactive use
+    in the simplest and most usual way, to print or display an
+    announcement including an appropriate copyright notice and a notice
+    that there is no warranty (or else, saying that you provide a
+    warranty) and that users may redistribute the program under these
+    conditions, and telling the user how to view a copy of this General
+    Public License.
+
+    d) You may charge a fee for the physical act of transferring a
+    copy, and you may at your option offer warranty protection in
+    exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+
+  3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal charge
+    for the cost of distribution) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it.  For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+  4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License.  However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+  5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions.  You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+
+  7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+  8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+        Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+  To do so, attach the following notices to the program.  It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19xx name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License.  Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  program `Gnomovision' (a program to direct compilers to make passes
+  at assemblers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+--- The Artistic License 1.0 ---
+
+This software is Copyright (c) 2022 by Jesse Luehrs.
+
+This is free software, licensed under:
+
+  The Artistic License 1.0
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of
+the package the right to use and distribute the Package in a more-or-less
+customary fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+  - "Package" refers to the collection of files distributed by the Copyright
+    Holder, and derivatives of that collection of files created through
+    textual modification.
+  - "Standard Version" refers to such a Package if it has not been modified,
+    or has been modified in accordance with the wishes of the Copyright
+    Holder.
+  - "Copyright Holder" is whoever is named in the copyright or copyrights for
+    the package.
+  - "You" is you, if you're thinking about copying or distributing this Package.
+  - "Reasonable copying fee" is whatever you can justify on the basis of media
+    cost, duplication charges, time of people involved, and so on. (You will
+    not be required to justify it to the Copyright Holder, but only to the
+    computing community at large as a market that must bear the fee.)
+  - "Freely Available" means that no fee is charged for the item itself, though
+    there may be fees involved in handling the item. It also means that
+    recipients of the item may redistribute it under the same conditions they
+    received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived
+from the Public Domain or from the Copyright Holder. A Package modified in such
+a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided that
+you insert a prominent notice in each changed file stating how and when you
+changed that file, and provided that you do at least ONE of the following:
+
+  a) place your modifications in the Public Domain or otherwise make them
+     Freely Available, such as by posting said modifications to Usenet or an
+     equivalent medium, or placing the modifications on a major archive site
+     such as ftp.uu.net, or by allowing the Copyright Holder to include your
+     modifications in the Standard Version of the Package.
+
+  b) use the modified Package only within your corporation or organization.
+
+  c) rename any non-standard executables so the names do not conflict with
+     standard executables, which must also be provided, and provide a separate
+     manual page for each non-standard executable that clearly documents how it
+     differs from the Standard Version.
+
+  d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+  a) distribute a Standard Version of the executables and library files,
+     together with instructions (in the manual page or equivalent) on where to
+     get the Standard Version.
+
+  b) accompany the distribution with the machine-readable source of the Package
+     with your modifications.
+
+  c) accompany any non-standard executables with their corresponding Standard
+     Version executables, giving the non-standard executables non-standard
+     names, and clearly documenting the differences in manual pages (or
+     equivalent), together with instructions on where to get the Standard
+     Version.
+
+  d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package.  You may charge any fee you choose for support of this Package. You
+may not charge a fee for this Package itself. However, you may distribute this
+Package in aggregate with other (possibly commercial) programs as part of a
+larger (possibly commercial) software distribution provided that you do not
+advertise this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output
+from the programs of this Package do not automatically fall under the copyright
+of this Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+The End
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..158d770
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,61 @@
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.024.
+CONTRIBUTING
+Changes
+INSTALL
+LICENSE
+MANIFEST
+META.json
+META.yml
+Makefile.PL
+README
+bin/package-stash-conflicts
+dist.ini
+inc/ExtUtils/HasCompiler.pm
+lib/Package/Stash.pm
+lib/Package/Stash/Conflicts.pm
+lib/Package/Stash/PP.pm
+t/00-report-prereqs.dd
+t/00-report-prereqs.t
+t/addsub.t
+t/anon-basic.t
+t/anon.t
+t/bare-anon-basic.t
+t/bare-anon.t
+t/basic.t
+t/compile-time.t
+t/edge-cases.t
+t/extension.t
+t/get.t
+t/impl-selection/basic-pp.t
+t/impl-selection/basic-xs.t
+t/impl-selection/bug-rt-78272.t
+t/impl-selection/choice.t
+t/impl-selection/env.t
+t/impl-selection/var.t
+t/io.t
+t/isa.t
+t/lib/CompileTime.pm
+t/lib/Package/Stash.pm
+t/magic.t
+t/paamayim_nekdotayim.t
+t/scalar-values.t
+t/stash-deletion.t
+t/synopsis.t
+t/warnings-taint.t
+t/warnings.t
+t/zzz-check-breaks.t
+xt/author/00-compile.t
+xt/author/distmeta.t
+xt/author/eol.t
+xt/author/kwalitee.t
+xt/author/leaks-debug.t
+xt/author/leaks.t
+xt/author/minimum-version.t
+xt/author/mojibake.t
+xt/author/no-tabs.t
+xt/author/pod-no404s.t
+xt/author/pod-spell.t
+xt/author/pod-syntax.t
+xt/author/portability.t
+xt/release/changes_has_content.t
+xt/release/cpan-changes.t
diff --git a/META.json b/META.json
new file mode 100644 (file)
index 0000000..0f27e67
--- /dev/null
+++ b/META.json
@@ -0,0 +1,1368 @@
+{
+   "abstract" : "Routines for manipulating stashes",
+   "author" : [
+      "Stevan Little <stevan.little@iinteractive.com>",
+      "Jesse Luehrs <doy@tozt.net>"
+   ],
+   "dynamic_config" : 1,
+   "generated_by" : "Dist::Zilla version 6.024, CPAN::Meta::Converter version 2.150010",
+   "license" : [
+      "perl_5"
+   ],
+   "meta-spec" : {
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+      "version" : 2
+   },
+   "name" : "Package-Stash",
+   "no_index" : {
+      "directory" : [
+         "t",
+         "xt"
+      ]
+   },
+   "prereqs" : {
+      "configure" : {
+         "requires" : {
+            "Dist::CheckConflicts" : "0.02",
+            "ExtUtils::MakeMaker" : "0",
+            "Text::ParseWords" : "0",
+            "perl" : "5.008001"
+         }
+      },
+      "develop" : {
+         "recommends" : {
+            "Dist::Zilla::PluginBundle::Author::ETHER" : "0.162",
+            "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007"
+         },
+         "requires" : {
+            "Encode" : "0",
+            "ExtUtils::HasCompiler" : "0.014",
+            "File::Spec" : "0",
+            "IO::Handle" : "0",
+            "IPC::Open3" : "0",
+            "Package::Anon" : "0",
+            "Pod::Wordlist" : "0",
+            "Test::CPAN::Changes" : "0.19",
+            "Test::CPAN::Meta" : "0",
+            "Test::EOL" : "0",
+            "Test::Fatal" : "0",
+            "Test::Kwalitee" : "1.21",
+            "Test::LeakTrace" : "0",
+            "Test::MinimumVersion" : "0",
+            "Test::Mojibake" : "0",
+            "Test::More" : "0.96",
+            "Test::NoTabs" : "0",
+            "Test::Pod" : "1.41",
+            "Test::Pod::No404s" : "0",
+            "Test::Portability::Files" : "0",
+            "Test::Spelling" : "0.12",
+            "Variable::Magic" : "0",
+            "lib" : "0"
+         }
+      },
+      "runtime" : {
+         "recommends" : {
+            "Package::Stash::XS" : "0.26"
+         },
+         "requires" : {
+            "B" : "0",
+            "Carp" : "0",
+            "Dist::CheckConflicts" : "0.02",
+            "Getopt::Long" : "0",
+            "Module::Implementation" : "0.06",
+            "Scalar::Util" : "0",
+            "Symbol" : "0",
+            "constant" : "0",
+            "perl" : "5.008001",
+            "strict" : "0",
+            "warnings" : "0"
+         }
+      },
+      "test" : {
+         "recommends" : {
+            "CPAN::Meta" : "2.120900"
+         },
+         "requires" : {
+            "CPAN::Meta::Check" : "0.011",
+            "CPAN::Meta::Requirements" : "0",
+            "ExtUtils::MakeMaker" : "0",
+            "File::Spec" : "0",
+            "Test::Fatal" : "0",
+            "Test::More" : "0.88",
+            "Test::Needs" : "0",
+            "base" : "0",
+            "lib" : "0",
+            "perl" : "5.008001"
+         },
+         "suggests" : {
+            "Variable::Magic" : "0"
+         }
+      },
+      "x_Dist_Zilla" : {
+         "requires" : {
+            "Dist::Zilla" : "5",
+            "Dist::Zilla::Plugin::Authority" : "1.009",
+            "Dist::Zilla::Plugin::AutoMetaResources" : "0",
+            "Dist::Zilla::Plugin::AutoPrereqs" : "5.038",
+            "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : "0.004",
+            "Dist::Zilla::Plugin::CheckIssues" : "0",
+            "Dist::Zilla::Plugin::CheckMetaResources" : "0",
+            "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0.019",
+            "Dist::Zilla::Plugin::CheckSelfDependency" : "0",
+            "Dist::Zilla::Plugin::CheckStrictVersion" : "0",
+            "Dist::Zilla::Plugin::ConfirmRelease" : "0",
+            "Dist::Zilla::Plugin::Conflicts" : "0",
+            "Dist::Zilla::Plugin::CopyFilesFromRelease" : "0",
+            "Dist::Zilla::Plugin::DynamicPrereqs" : "0.029",
+            "Dist::Zilla::Plugin::EnsureLatestPerl" : "0",
+            "Dist::Zilla::Plugin::ExecDir" : "0",
+            "Dist::Zilla::Plugin::FileFinder::ByName" : "0",
+            "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : "0",
+            "Dist::Zilla::Plugin::Git::Check" : "0",
+            "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0.004",
+            "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts" : "0",
+            "Dist::Zilla::Plugin::Git::Commit" : "2.020",
+            "Dist::Zilla::Plugin::Git::Contributors" : "0.029",
+            "Dist::Zilla::Plugin::Git::Describe" : "0.004",
+            "Dist::Zilla::Plugin::Git::GatherDir" : "2.016",
+            "Dist::Zilla::Plugin::Git::Push" : "0",
+            "Dist::Zilla::Plugin::Git::Remote::Check" : "0",
+            "Dist::Zilla::Plugin::Git::Tag" : "0",
+            "Dist::Zilla::Plugin::GitHub::Update" : "0.40",
+            "Dist::Zilla::Plugin::GithubMeta" : "0.54",
+            "Dist::Zilla::Plugin::InstallGuide" : "1.200005",
+            "Dist::Zilla::Plugin::Keywords" : "0.004",
+            "Dist::Zilla::Plugin::License" : "5.038",
+            "Dist::Zilla::Plugin::MakeMaker" : "0",
+            "Dist::Zilla::Plugin::Manifest" : "0",
+            "Dist::Zilla::Plugin::MetaConfig" : "0",
+            "Dist::Zilla::Plugin::MetaJSON" : "0",
+            "Dist::Zilla::Plugin::MetaNoIndex" : "0",
+            "Dist::Zilla::Plugin::MetaProvides::Package" : "1.15000002",
+            "Dist::Zilla::Plugin::MetaTests" : "0",
+            "Dist::Zilla::Plugin::MetaYAML" : "0",
+            "Dist::Zilla::Plugin::MinimumPerl" : "1.006",
+            "Dist::Zilla::Plugin::MojibakeTests" : "0.8",
+            "Dist::Zilla::Plugin::NextRelease" : "5.033",
+            "Dist::Zilla::Plugin::PodSyntaxTests" : "5.040",
+            "Dist::Zilla::Plugin::Prereqs" : "0",
+            "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0.006",
+            "Dist::Zilla::Plugin::Prereqs::Soften" : "0",
+            "Dist::Zilla::Plugin::PromptIfStale" : "0",
+            "Dist::Zilla::Plugin::Readme" : "0",
+            "Dist::Zilla::Plugin::ReadmeAnyFromPod" : "0.142180",
+            "Dist::Zilla::Plugin::RewriteVersion::Transitional" : "0.006",
+            "Dist::Zilla::Plugin::Run::AfterBuild" : "0.041",
+            "Dist::Zilla::Plugin::Run::AfterRelease" : "0.038",
+            "Dist::Zilla::Plugin::RunExtraTests" : "0.024",
+            "Dist::Zilla::Plugin::StaticInstall" : "0.005",
+            "Dist::Zilla::Plugin::SurgicalPodWeaver" : "0",
+            "Dist::Zilla::Plugin::Test::CPAN::Changes" : "0.012",
+            "Dist::Zilla::Plugin::Test::ChangesHasContent" : "0",
+            "Dist::Zilla::Plugin::Test::CheckBreaks" : "0",
+            "Dist::Zilla::Plugin::Test::Compile" : "2.039",
+            "Dist::Zilla::Plugin::Test::EOL" : "0.17",
+            "Dist::Zilla::Plugin::Test::Kwalitee" : "2.10",
+            "Dist::Zilla::Plugin::Test::MinimumVersion" : "2.000010",
+            "Dist::Zilla::Plugin::Test::NoTabs" : "0.08",
+            "Dist::Zilla::Plugin::Test::Pod::No404s" : "1.003",
+            "Dist::Zilla::Plugin::Test::PodSpelling" : "2.006003",
+            "Dist::Zilla::Plugin::Test::Portability" : "2.000007",
+            "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0.022",
+            "Dist::Zilla::Plugin::TestRelease" : "0",
+            "Dist::Zilla::Plugin::UploadToCPAN" : "0",
+            "Dist::Zilla::Plugin::UseUnsafeInc" : "0",
+            "Dist::Zilla::PluginBundle::Author::ETHER" : "0.119",
+            "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007",
+            "ExtUtils::HasCompiler" : "0.014",
+            "Software::License::Perl_5" : "0"
+         }
+      }
+   },
+   "provides" : {
+      "Package::Stash" : {
+         "file" : "lib/Package/Stash.pm",
+         "version" : "0.40"
+      },
+      "Package::Stash::PP" : {
+         "file" : "lib/Package/Stash/PP.pm",
+         "version" : "0.40"
+      }
+   },
+   "release_status" : "stable",
+   "resources" : {
+      "bugtracker" : {
+         "mailto" : "bug-Package-Stash@rt.cpan.org",
+         "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Package-Stash"
+      },
+      "homepage" : "https://github.com/moose/Package-Stash",
+      "repository" : {
+         "type" : "git",
+         "url" : "https://github.com/moose/Package-Stash.git",
+         "web" : "https://github.com/moose/Package-Stash"
+      }
+   },
+   "version" : "0.40",
+   "x_Dist_Zilla" : {
+      "perl" : {
+         "version" : "5.035008"
+      },
+      "plugins" : [
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "develop",
+                  "type" : "recommends"
+               }
+            },
+            "name" : "@Author::ETHER/pluginbundle version",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::PromptIfStale",
+            "config" : {
+               "Dist::Zilla::Plugin::PromptIfStale" : {
+                  "check_all_plugins" : 0,
+                  "check_all_prereqs" : 0,
+                  "modules" : [
+                     "Dist::Zilla::PluginBundle::Author::ETHER"
+                  ],
+                  "phase" : "build",
+                  "run_under_travis" : 0,
+                  "skip" : []
+               }
+            },
+            "name" : "@Author::ETHER/stale modules, build",
+            "version" : "0.057"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::ExecDir",
+            "name" : "@Author::ETHER/ExecDir",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FileFinder::ByName",
+            "name" : "@Author::ETHER/Examples",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::GatherDir",
+            "config" : {
+               "Dist::Zilla::Plugin::GatherDir" : {
+                  "exclude_filename" : [
+                     "CONTRIBUTING",
+                     "INSTALL",
+                     "LICENSE",
+                     "README.pod"
+                  ],
+                  "exclude_match" : [],
+                  "follow_symlinks" : 0,
+                  "include_dotfiles" : 0,
+                  "prefix" : "",
+                  "prune_directory" : [],
+                  "root" : "."
+               },
+               "Dist::Zilla::Plugin::Git::GatherDir" : {
+                  "include_untracked" : 0
+               }
+            },
+            "name" : "@Author::ETHER/Git::GatherDir",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaYAML",
+            "name" : "@Author::ETHER/MetaYAML",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaJSON",
+            "name" : "@Author::ETHER/MetaJSON",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Readme",
+            "name" : "@Author::ETHER/Readme",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Manifest",
+            "name" : "@Author::ETHER/Manifest",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::License",
+            "name" : "@Author::ETHER/License",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::GenerateFile::FromShareDir",
+            "config" : {
+               "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : {
+                  "destination_filename" : "CONTRIBUTING",
+                  "dist" : "Dist-Zilla-PluginBundle-Author-ETHER",
+                  "encoding" : "UTF-8",
+                  "has_xs" : 0,
+                  "location" : "build",
+                  "source_filename" : "CONTRIBUTING"
+               },
+               "Dist::Zilla::Role::RepoFileInjector" : {
+                  "allow_overwrite" : 1,
+                  "repo_root" : ".",
+                  "version" : "0.009"
+               }
+            },
+            "name" : "@Author::ETHER/generate CONTRIBUTING",
+            "version" : "0.015"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::InstallGuide",
+            "config" : {
+               "Dist::Zilla::Role::ModuleMetadata" : {
+                  "Module::Metadata" : "1.000037",
+                  "version" : "0.006"
+               }
+            },
+            "name" : "@Author::ETHER/InstallGuide",
+            "version" : "1.200014"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::Compile",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::Compile" : {
+                  "bail_out_on_fail" : 1,
+                  "fail_on_warning" : "author",
+                  "fake_home" : 0,
+                  "filename" : "xt/author/00-compile.t",
+                  "module_finder" : [
+                     ":InstallModules"
+                  ],
+                  "needs_display" : 0,
+                  "phase" : "develop",
+                  "script_finder" : [
+                     ":PerlExecFiles",
+                     "@Author::ETHER/Examples"
+                  ],
+                  "skips" : [],
+                  "switch" : []
+               }
+            },
+            "name" : "@Author::ETHER/Test::Compile",
+            "version" : "2.058"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::NoTabs",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::NoTabs" : {
+                  "filename" : "xt/author/no-tabs.t",
+                  "finder" : [
+                     ":InstallModules",
+                     ":ExecFiles",
+                     "@Author::ETHER/Examples",
+                     ":TestFiles",
+                     ":ExtraTestFiles"
+                  ]
+               }
+            },
+            "name" : "@Author::ETHER/Test::NoTabs",
+            "version" : "0.15"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::EOL",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::EOL" : {
+                  "filename" : "xt/author/eol.t",
+                  "finder" : [
+                     ":ExecFiles",
+                     ":ExtraTestFiles",
+                     ":InstallModules",
+                     ":TestFiles",
+                     "@Author::ETHER/Examples"
+                  ],
+                  "trailing_whitespace" : 1
+               }
+            },
+            "name" : "@Author::ETHER/Test::EOL",
+            "version" : "0.19"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaTests",
+            "name" : "@Author::ETHER/MetaTests",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::CPAN::Changes" : {
+                  "changelog" : "Changes"
+               }
+            },
+            "name" : "@Author::ETHER/Test::CPAN::Changes",
+            "version" : "0.012"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent",
+            "name" : "@Author::ETHER/Test::ChangesHasContent",
+            "version" : "0.011"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::MinimumVersion",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::MinimumVersion" : {
+                  "max_target_perl" : "5.008001"
+               }
+            },
+            "name" : "@Author::ETHER/Test::MinimumVersion",
+            "version" : "2.000010"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::PodSyntaxTests",
+            "name" : "@Author::ETHER/PodSyntaxTests",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::PodSpelling",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::PodSpelling" : {
+                  "directories" : [
+                     "examples",
+                     "lib",
+                     "script",
+                     "t",
+                     "xt"
+                  ],
+                  "spell_cmd" : "",
+                  "stopwords" : [
+                     "irc"
+                  ],
+                  "wordlist" : "Pod::Wordlist"
+               }
+            },
+            "name" : "@Author::ETHER/Test::PodSpelling",
+            "version" : "2.007005"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::Pod::No404s",
+            "name" : "@Author::ETHER/Test::Pod::No404s",
+            "version" : "1.004"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::Kwalitee",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::Kwalitee" : {
+                  "filename" : "xt/author/kwalitee.t",
+                  "skiptest" : []
+               }
+            },
+            "name" : "@Author::ETHER/Test::Kwalitee",
+            "version" : "2.12"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MojibakeTests",
+            "name" : "@Author::ETHER/MojibakeTests",
+            "version" : "0.8"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs",
+            "name" : "@Author::ETHER/Test::ReportPrereqs",
+            "version" : "0.028"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::Portability",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::Portability" : {
+                  "options" : ""
+               }
+            },
+            "name" : "@Author::ETHER/Test::Portability",
+            "version" : "2.001000"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Describe",
+            "name" : "@Author::ETHER/Git::Describe",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::SurgicalPodWeaver",
+            "config" : {
+               "Dist::Zilla::Plugin::PodWeaver" : {
+                  "config_plugins" : [
+                     "@Author::ETHER"
+                  ],
+                  "finder" : [
+                     ":InstallModules",
+                     ":ExecFiles"
+                  ],
+                  "plugins" : [
+                     {
+                        "class" : "Pod::Weaver::Plugin::EnsurePod5",
+                        "name" : "@Author::ETHER/EnsurePod5",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Plugin::H1Nester",
+                        "name" : "@Author::ETHER/H1Nester",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Plugin::SingleEncoding",
+                        "name" : "@Author::ETHER/SingleEncoding",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Plugin::Transformer",
+                        "name" : "@Author::ETHER/List",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Plugin::Transformer",
+                        "name" : "@Author::ETHER/Verbatim",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Region",
+                        "name" : "@Author::ETHER/header",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Name",
+                        "name" : "@Author::ETHER/Name",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Version",
+                        "name" : "@Author::ETHER/Version",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Region",
+                        "name" : "@Author::ETHER/prelude",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Generic",
+                        "name" : "SYNOPSIS",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Generic",
+                        "name" : "DESCRIPTION",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Generic",
+                        "name" : "OVERVIEW",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Collect",
+                        "name" : "ATTRIBUTES",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Collect",
+                        "name" : "METHODS",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Collect",
+                        "name" : "FUNCTIONS",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Collect",
+                        "name" : "TYPES",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Leftovers",
+                        "name" : "@Author::ETHER/Leftovers",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Region",
+                        "name" : "@Author::ETHER/postlude",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::GenerateSection",
+                        "name" : "@Author::ETHER/generate SUPPORT",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Authors",
+                        "name" : "@Author::ETHER/Authors",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::AllowOverride",
+                        "name" : "@Author::ETHER/allow override AUTHOR",
+                        "version" : "0.05"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Contributors",
+                        "name" : "@Author::ETHER/Contributors",
+                        "version" : "0.009"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Legal",
+                        "name" : "@Author::ETHER/Legal",
+                        "version" : "4.018"
+                     },
+                     {
+                        "class" : "Pod::Weaver::Section::Region",
+                        "name" : "@Author::ETHER/footer",
+                        "version" : "4.018"
+                     }
+                  ]
+               }
+            },
+            "name" : "@Author::ETHER/SurgicalPodWeaver",
+            "version" : "0.0023"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::GithubMeta",
+            "name" : "@Author::ETHER/GithubMeta",
+            "version" : "0.58"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::AutoMetaResources",
+            "name" : "@Author::ETHER/AutoMetaResources",
+            "version" : "1.21"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Authority",
+            "name" : "@Author::ETHER/Authority",
+            "version" : "1.009"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaNoIndex",
+            "name" : "@Author::ETHER/MetaNoIndex",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaProvides::Package",
+            "config" : {
+               "Dist::Zilla::Plugin::MetaProvides::Package" : {
+                  "finder" : [
+                     ":InstallModules"
+                  ],
+                  "finder_objects" : [
+                     {
+                        "class" : "Dist::Zilla::Plugin::FinderCode",
+                        "name" : ":InstallModules",
+                        "version" : "6.024"
+                     }
+                  ],
+                  "include_underscores" : 0
+               },
+               "Dist::Zilla::Role::MetaProvider::Provider" : {
+                  "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004",
+                  "inherit_missing" : 0,
+                  "inherit_version" : 0,
+                  "meta_noindex" : 1
+               },
+               "Dist::Zilla::Role::ModuleMetadata" : {
+                  "Module::Metadata" : "1.000037",
+                  "version" : "0.006"
+               }
+            },
+            "name" : "@Author::ETHER/MetaProvides::Package",
+            "version" : "2.004003"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaConfig",
+            "name" : "@Author::ETHER/MetaConfig",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Keywords",
+            "config" : {
+               "Dist::Zilla::Plugin::Keywords" : {
+                  "keywords" : []
+               }
+            },
+            "name" : "@Author::ETHER/Keywords",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::UseUnsafeInc",
+            "config" : {
+               "Dist::Zilla::Plugin::UseUnsafeInc" : {
+                  "dot_in_INC" : 0
+               }
+            },
+            "name" : "@Author::ETHER/UseUnsafeInc",
+            "version" : "0.001"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::AutoPrereqs",
+            "name" : "@Author::ETHER/AutoPrereqs",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs::AuthorDeps",
+            "name" : "@Author::ETHER/Prereqs::AuthorDeps",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MinimumPerl",
+            "name" : "@Author::ETHER/MinimumPerl",
+            "version" : "1.006"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "x_Dist_Zilla",
+                  "type" : "requires"
+               }
+            },
+            "name" : "@Author::ETHER/pod_weaving",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MakeMaker",
+            "config" : {
+               "Dist::Zilla::Role::TestRunner" : {
+                  "default_jobs" : 9
+               }
+            },
+            "name" : "@Author::ETHER/MakeMaker",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Contributors",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Contributors" : {
+                  "git_version" : "2.34.1",
+                  "include_authors" : 0,
+                  "include_releaser" : 1,
+                  "order_by" : "commits",
+                  "paths" : []
+               }
+            },
+            "name" : "@Author::ETHER/Git::Contributors",
+            "version" : "0.036"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::StaticInstall",
+            "config" : {
+               "Dist::Zilla::Plugin::StaticInstall" : {
+                  "dry_run" : 1,
+                  "mode" : "auto"
+               }
+            },
+            "name" : "@Author::ETHER/StaticInstall",
+            "version" : "0.012"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::RunExtraTests",
+            "config" : {
+               "Dist::Zilla::Role::TestRunner" : {
+                  "default_jobs" : 9
+               }
+            },
+            "name" : "@Author::ETHER/RunExtraTests",
+            "version" : "0.029"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CheckSelfDependency",
+            "config" : {
+               "Dist::Zilla::Plugin::CheckSelfDependency" : {
+                  "finder" : [
+                     ":InstallModules"
+                  ]
+               },
+               "Dist::Zilla::Role::ModuleMetadata" : {
+                  "Module::Metadata" : "1.000037",
+                  "version" : "0.006"
+               }
+            },
+            "name" : "@Author::ETHER/CheckSelfDependency",
+            "version" : "0.011"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Run::AfterBuild",
+            "config" : {
+               "Dist::Zilla::Plugin::Run::Role::Runner" : {
+                  "fatal_errors" : 1,
+                  "quiet" : 1,
+                  "run" : [
+                     "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\""
+                  ],
+                  "version" : "0.048"
+               }
+            },
+            "name" : "@Author::ETHER/.ackrc",
+            "version" : "0.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Run::AfterBuild",
+            "config" : {
+               "Dist::Zilla::Plugin::Run::Role::Runner" : {
+                  "eval" : [
+                     "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }"
+                  ],
+                  "fatal_errors" : 0,
+                  "quiet" : 1,
+                  "version" : "0.048"
+               }
+            },
+            "name" : "@Author::ETHER/.latest",
+            "version" : "0.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CheckStrictVersion",
+            "name" : "@Author::ETHER/CheckStrictVersion",
+            "version" : "0.001"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CheckMetaResources",
+            "name" : "@Author::ETHER/CheckMetaResources",
+            "version" : "0.001"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::EnsureLatestPerl",
+            "config" : {
+               "Dist::Zilla::Plugin::EnsureLatestPerl" : {
+                  "Module::CoreList" : "5.20220120"
+               }
+            },
+            "name" : "@Author::ETHER/EnsureLatestPerl",
+            "version" : "0.008"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::PromptIfStale",
+            "config" : {
+               "Dist::Zilla::Plugin::PromptIfStale" : {
+                  "check_all_plugins" : 1,
+                  "check_all_prereqs" : 1,
+                  "modules" : [],
+                  "phase" : "release",
+                  "run_under_travis" : 0,
+                  "skip" : []
+               }
+            },
+            "name" : "@Author::ETHER/stale modules, release",
+            "version" : "0.057"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Check",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Check" : {
+                  "untracked_files" : "die"
+               },
+               "Dist::Zilla::Role::Git::DirtyFiles" : {
+                  "allow_dirty" : [],
+                  "allow_dirty_match" : [],
+                  "changelog" : "Changes"
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               }
+            },
+            "name" : "@Author::ETHER/initial check",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts",
+            "config" : {
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               }
+            },
+            "name" : "@Author::ETHER/Git::CheckFor::MergeConflicts",
+            "version" : "0.014"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch",
+            "config" : {
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               }
+            },
+            "name" : "@Author::ETHER/Git::CheckFor::CorrectBranch",
+            "version" : "0.014"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Remote::Check",
+            "name" : "@Author::ETHER/Git::Remote::Check",
+            "version" : "0.1.2"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed",
+            "name" : "@Author::ETHER/CheckPrereqsIndexed",
+            "version" : "0.021"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::TestRelease",
+            "name" : "@Author::ETHER/TestRelease",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Check",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Check" : {
+                  "untracked_files" : "die"
+               },
+               "Dist::Zilla::Role::Git::DirtyFiles" : {
+                  "allow_dirty" : [],
+                  "allow_dirty_match" : [],
+                  "changelog" : "Changes"
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               }
+            },
+            "name" : "@Author::ETHER/after tests",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CheckIssues",
+            "name" : "@Author::ETHER/CheckIssues",
+            "version" : "0.011"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::UploadToCPAN",
+            "name" : "@Author::ETHER/UploadToCPAN",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease",
+            "config" : {
+               "Dist::Zilla::Plugin::CopyFilesFromRelease" : {
+                  "filename" : [
+                     "CONTRIBUTING",
+                     "INSTALL",
+                     "LICENCE",
+                     "LICENSE",
+                     "ppport.h"
+                  ],
+                  "match" : []
+               }
+            },
+            "name" : "@Author::ETHER/copy generated files",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod",
+            "config" : {
+               "Dist::Zilla::Role::FileWatcher" : {
+                  "version" : "0.006"
+               }
+            },
+            "name" : "@Author::ETHER/ReadmeAnyFromPod",
+            "version" : "0.163250"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "develop",
+                  "type" : "recommends"
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/pluginbundle version",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional",
+            "config" : {
+               "Dist::Zilla::Plugin::RewriteVersion" : {
+                  "add_tarball_name" : 0,
+                  "finders" : [
+                     ":ExecFiles",
+                     ":InstallModules"
+                  ],
+                  "global" : 1,
+                  "skip_version_provider" : 0
+               },
+               "Dist::Zilla::Plugin::RewriteVersion::Transitional" : {}
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional",
+            "version" : "0.009"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::MetaProvides::Update",
+            "name" : "@Author::ETHER/@Git::VersionManager/MetaProvides::Update",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease",
+            "config" : {
+               "Dist::Zilla::Plugin::CopyFilesFromRelease" : {
+                  "filename" : [
+                     "Changes"
+                  ],
+                  "match" : []
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease",
+            "version" : "0.007"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Commit",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Commit" : {
+                  "add_files_in" : [
+                     "."
+                  ],
+                  "commit_msg" : "%N-%v%t%n%n%c",
+                  "signoff" : 0
+               },
+               "Dist::Zilla::Role::Git::DirtyFiles" : {
+                  "allow_dirty" : [
+                     "CONTRIBUTING",
+                     "Changes",
+                     "INSTALL",
+                     "LICENSE",
+                     "README.pod"
+                  ],
+                  "allow_dirty_match" : [],
+                  "changelog" : "Changes"
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               },
+               "Dist::Zilla::Role::Git::StringFormatter" : {
+                  "time_zone" : "local"
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/release snapshot",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Tag",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Tag" : {
+                  "branch" : null,
+                  "changelog" : "Changes",
+                  "signed" : 0,
+                  "tag" : "v0.40",
+                  "tag_format" : "v%V",
+                  "tag_message" : "v%v%t"
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               },
+               "Dist::Zilla::Role::Git::StringFormatter" : {
+                  "time_zone" : "local"
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/Git::Tag",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional",
+            "config" : {
+               "Dist::Zilla::Plugin::BumpVersionAfterRelease" : {
+                  "finders" : [
+                     ":InstallModules"
+                  ],
+                  "global" : 1,
+                  "munge_makefile_pl" : 1
+               },
+               "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {}
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional",
+            "version" : "0.009"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::NextRelease",
+            "name" : "@Author::ETHER/@Git::VersionManager/NextRelease",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Commit",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Commit" : {
+                  "add_files_in" : [],
+                  "commit_msg" : "increment $VERSION after %v release",
+                  "signoff" : 0
+               },
+               "Dist::Zilla::Role::Git::DirtyFiles" : {
+                  "allow_dirty" : [
+                     "Build.PL",
+                     "Changes",
+                     "Makefile.PL"
+                  ],
+                  "allow_dirty_match" : [
+                     "(?^:^lib/.*\\.pm$)"
+                  ],
+                  "changelog" : "Changes"
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               },
+               "Dist::Zilla::Role::Git::StringFormatter" : {
+                  "time_zone" : "local"
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/post-release commit",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "x_Dist_Zilla",
+                  "type" : "requires"
+               }
+            },
+            "name" : "@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Git::Push",
+            "config" : {
+               "Dist::Zilla::Plugin::Git::Push" : {
+                  "push_to" : [
+                     "origin"
+                  ],
+                  "remotes_must_exist" : 1
+               },
+               "Dist::Zilla::Role::Git::Repo" : {
+                  "git_version" : "2.34.1",
+                  "repo_root" : "."
+               }
+            },
+            "name" : "@Author::ETHER/Git::Push",
+            "version" : "2.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::GitHub::Update",
+            "config" : {
+               "Dist::Zilla::Plugin::GitHub::Update" : {
+                  "metacpan" : 1
+               }
+            },
+            "name" : "@Author::ETHER/GitHub::Update",
+            "version" : "0.48"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Run::AfterRelease",
+            "config" : {
+               "Dist::Zilla::Plugin::Run::Role::Runner" : {
+                  "fatal_errors" : 0,
+                  "quiet" : 0,
+                  "run" : [
+                     "REDACTED"
+                  ],
+                  "version" : "0.048"
+               }
+            },
+            "name" : "@Author::ETHER/install release",
+            "version" : "0.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Run::AfterRelease",
+            "config" : {
+               "Dist::Zilla::Plugin::Run::Role::Runner" : {
+                  "eval" : [
+                     "print \"release complete!\\xa\""
+                  ],
+                  "fatal_errors" : 1,
+                  "quiet" : 1,
+                  "version" : "0.048"
+               }
+            },
+            "name" : "@Author::ETHER/release complete",
+            "version" : "0.048"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::ConfirmRelease",
+            "name" : "@Author::ETHER/ConfirmRelease",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "x_Dist_Zilla",
+                  "type" : "requires"
+               }
+            },
+            "name" : "@Author::ETHER/prereqs for @Author::ETHER",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::DynamicPrereqs",
+            "config" : {
+               "Dist::Zilla::Role::ModuleMetadata" : {
+                  "Module::Metadata" : "1.000037",
+                  "version" : "0.006"
+               }
+            },
+            "name" : "DynamicPrereqs",
+            "version" : "0.039"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "test",
+                  "type" : "requires"
+               }
+            },
+            "name" : "TestRequires",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "runtime",
+                  "type" : "recommends"
+               }
+            },
+            "name" : "RuntimeRecommends",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs" : {
+                  "phase" : "develop",
+                  "type" : "requires"
+               }
+            },
+            "name" : "DevelopRequires",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Prereqs::Soften",
+            "config" : {
+               "Dist::Zilla::Plugin::Prereqs::Soften" : {
+                  "copy_to" : [
+                     "develop.requires"
+                  ],
+                  "modules" : [
+                     "Variable::Magic"
+                  ],
+                  "modules_from_features" : null,
+                  "to_relationship" : "suggests"
+               }
+            },
+            "name" : "Prereqs::Soften",
+            "version" : "0.006003"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Test::CheckBreaks",
+            "config" : {
+               "Dist::Zilla::Plugin::Test::CheckBreaks" : {
+                  "conflicts_module" : [
+                     "Module::Runtime::Conflicts",
+                     "Moose::Conflicts",
+                     "Package::Stash::Conflicts"
+                  ],
+                  "no_forced_deps" : 0
+               },
+               "Dist::Zilla::Role::ModuleMetadata" : {
+                  "Module::Metadata" : "1.000037",
+                  "version" : "0.006"
+               }
+            },
+            "name" : "Test::CheckBreaks",
+            "version" : "0.019"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::Conflicts",
+            "name" : "Conflicts",
+            "version" : "0.19"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":InstallModules",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":IncModules",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":TestFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":ExtraTestFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":ExecFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":PerlExecFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":ShareFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":MainModule",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":AllFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::FinderCode",
+            "name" : ":NoFiles",
+            "version" : "6.024"
+         },
+         {
+            "class" : "Dist::Zilla::Plugin::VerifyPhases",
+            "name" : "@Author::ETHER/PHASE VERIFICATION",
+            "version" : "0.016"
+         }
+      ],
+      "zilla" : {
+         "class" : "Dist::Zilla::Dist::Builder",
+         "config" : {
+            "is_trial" : 0
+         },
+         "version" : "6.024"
+      }
+   },
+   "x_authority" : "cpan:STEVAN",
+   "x_breaks" : {
+      "Class::MOP" : "<= 1.08",
+      "MooseX::Method::Signatures" : "<= 0.36",
+      "MooseX::Role::WithOverloading" : "<= 0.08",
+      "namespace::clean" : "<= 0.18"
+   },
+   "x_contributors" : [
+      "Karen Etheridge <ether@cpan.org>",
+      "Carlos Lima <carlos@multi>",
+      "Christian Walde <walde.christian@googlemail.com>",
+      "Dave Rolsky <autarch@urth.org>",
+      "Justin Hunter <justin.d.hunter@gmail.com>",
+      "Kent Fredric <kentfredric@gmail.com>",
+      "Niko Tyni <ntyni@debian.org>",
+      "Renee <reb@perl-services.de>",
+      "Tim Bunce <Tim.Bunce@pobox.com>"
+   ],
+   "x_generated_by_perl" : "v5.35.8",
+   "x_serialization_backend" : "Cpanel::JSON::XS version 4.27",
+   "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later",
+   "x_use_unsafe_inc" : 0
+}
+
diff --git a/META.yml b/META.yml
new file mode 100644 (file)
index 0000000..22afa65
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,938 @@
+---
+abstract: 'Routines for manipulating stashes'
+author:
+  - 'Stevan Little <stevan.little@iinteractive.com>'
+  - 'Jesse Luehrs <doy@tozt.net>'
+build_requires:
+  CPAN::Meta::Check: '0.011'
+  CPAN::Meta::Requirements: '0'
+  ExtUtils::MakeMaker: '0'
+  File::Spec: '0'
+  Test::Fatal: '0'
+  Test::More: '0.88'
+  Test::Needs: '0'
+  base: '0'
+  lib: '0'
+  perl: '5.008001'
+configure_requires:
+  Dist::CheckConflicts: '0.02'
+  ExtUtils::MakeMaker: '0'
+  Text::ParseWords: '0'
+  perl: '5.008001'
+dynamic_config: 1
+generated_by: 'Dist::Zilla version 6.024, 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: Package-Stash
+no_index:
+  directory:
+    - t
+    - xt
+provides:
+  Package::Stash:
+    file: lib/Package/Stash.pm
+    version: '0.40'
+  Package::Stash::PP:
+    file: lib/Package/Stash/PP.pm
+    version: '0.40'
+recommends:
+  Package::Stash::XS: '0.26'
+requires:
+  B: '0'
+  Carp: '0'
+  Dist::CheckConflicts: '0.02'
+  Getopt::Long: '0'
+  Module::Implementation: '0.06'
+  Scalar::Util: '0'
+  Symbol: '0'
+  constant: '0'
+  perl: '5.008001'
+  strict: '0'
+  warnings: '0'
+resources:
+  bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Package-Stash
+  homepage: https://github.com/moose/Package-Stash
+  repository: https://github.com/moose/Package-Stash.git
+version: '0.40'
+x_Dist_Zilla:
+  perl:
+    version: '5.035008'
+  plugins:
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: develop
+          type: recommends
+      name: '@Author::ETHER/pluginbundle version'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::PromptIfStale
+      config:
+        Dist::Zilla::Plugin::PromptIfStale:
+          check_all_plugins: 0
+          check_all_prereqs: 0
+          modules:
+            - Dist::Zilla::PluginBundle::Author::ETHER
+          phase: build
+          run_under_travis: 0
+          skip: []
+      name: '@Author::ETHER/stale modules, build'
+      version: '0.057'
+    -
+      class: Dist::Zilla::Plugin::ExecDir
+      name: '@Author::ETHER/ExecDir'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FileFinder::ByName
+      name: '@Author::ETHER/Examples'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Git::GatherDir
+      config:
+        Dist::Zilla::Plugin::GatherDir:
+          exclude_filename:
+            - CONTRIBUTING
+            - INSTALL
+            - LICENSE
+            - README.pod
+          exclude_match: []
+          follow_symlinks: 0
+          include_dotfiles: 0
+          prefix: ''
+          prune_directory: []
+          root: .
+        Dist::Zilla::Plugin::Git::GatherDir:
+          include_untracked: 0
+      name: '@Author::ETHER/Git::GatherDir'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::MetaYAML
+      name: '@Author::ETHER/MetaYAML'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::MetaJSON
+      name: '@Author::ETHER/MetaJSON'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Readme
+      name: '@Author::ETHER/Readme'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Manifest
+      name: '@Author::ETHER/Manifest'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::License
+      name: '@Author::ETHER/License'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::GenerateFile::FromShareDir
+      config:
+        Dist::Zilla::Plugin::GenerateFile::FromShareDir:
+          destination_filename: CONTRIBUTING
+          dist: Dist-Zilla-PluginBundle-Author-ETHER
+          encoding: UTF-8
+          has_xs: 0
+          location: build
+          source_filename: CONTRIBUTING
+        Dist::Zilla::Role::RepoFileInjector:
+          allow_overwrite: 1
+          repo_root: .
+          version: '0.009'
+      name: '@Author::ETHER/generate CONTRIBUTING'
+      version: '0.015'
+    -
+      class: Dist::Zilla::Plugin::InstallGuide
+      config:
+        Dist::Zilla::Role::ModuleMetadata:
+          Module::Metadata: '1.000037'
+          version: '0.006'
+      name: '@Author::ETHER/InstallGuide'
+      version: '1.200014'
+    -
+      class: Dist::Zilla::Plugin::Test::Compile
+      config:
+        Dist::Zilla::Plugin::Test::Compile:
+          bail_out_on_fail: '1'
+          fail_on_warning: author
+          fake_home: 0
+          filename: xt/author/00-compile.t
+          module_finder:
+            - ':InstallModules'
+          needs_display: 0
+          phase: develop
+          script_finder:
+            - ':PerlExecFiles'
+            - '@Author::ETHER/Examples'
+          skips: []
+          switch: []
+      name: '@Author::ETHER/Test::Compile'
+      version: '2.058'
+    -
+      class: Dist::Zilla::Plugin::Test::NoTabs
+      config:
+        Dist::Zilla::Plugin::Test::NoTabs:
+          filename: xt/author/no-tabs.t
+          finder:
+            - ':InstallModules'
+            - ':ExecFiles'
+            - '@Author::ETHER/Examples'
+            - ':TestFiles'
+            - ':ExtraTestFiles'
+      name: '@Author::ETHER/Test::NoTabs'
+      version: '0.15'
+    -
+      class: Dist::Zilla::Plugin::Test::EOL
+      config:
+        Dist::Zilla::Plugin::Test::EOL:
+          filename: xt/author/eol.t
+          finder:
+            - ':ExecFiles'
+            - ':ExtraTestFiles'
+            - ':InstallModules'
+            - ':TestFiles'
+            - '@Author::ETHER/Examples'
+          trailing_whitespace: 1
+      name: '@Author::ETHER/Test::EOL'
+      version: '0.19'
+    -
+      class: Dist::Zilla::Plugin::MetaTests
+      name: '@Author::ETHER/MetaTests'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Test::CPAN::Changes
+      config:
+        Dist::Zilla::Plugin::Test::CPAN::Changes:
+          changelog: Changes
+      name: '@Author::ETHER/Test::CPAN::Changes'
+      version: '0.012'
+    -
+      class: Dist::Zilla::Plugin::Test::ChangesHasContent
+      name: '@Author::ETHER/Test::ChangesHasContent'
+      version: '0.011'
+    -
+      class: Dist::Zilla::Plugin::Test::MinimumVersion
+      config:
+        Dist::Zilla::Plugin::Test::MinimumVersion:
+          max_target_perl: '5.008001'
+      name: '@Author::ETHER/Test::MinimumVersion'
+      version: '2.000010'
+    -
+      class: Dist::Zilla::Plugin::PodSyntaxTests
+      name: '@Author::ETHER/PodSyntaxTests'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Test::PodSpelling
+      config:
+        Dist::Zilla::Plugin::Test::PodSpelling:
+          directories:
+            - examples
+            - lib
+            - script
+            - t
+            - xt
+          spell_cmd: ''
+          stopwords:
+            - irc
+          wordlist: Pod::Wordlist
+      name: '@Author::ETHER/Test::PodSpelling'
+      version: '2.007005'
+    -
+      class: Dist::Zilla::Plugin::Test::Pod::No404s
+      name: '@Author::ETHER/Test::Pod::No404s'
+      version: '1.004'
+    -
+      class: Dist::Zilla::Plugin::Test::Kwalitee
+      config:
+        Dist::Zilla::Plugin::Test::Kwalitee:
+          filename: xt/author/kwalitee.t
+          skiptest: []
+      name: '@Author::ETHER/Test::Kwalitee'
+      version: '2.12'
+    -
+      class: Dist::Zilla::Plugin::MojibakeTests
+      name: '@Author::ETHER/MojibakeTests'
+      version: '0.8'
+    -
+      class: Dist::Zilla::Plugin::Test::ReportPrereqs
+      name: '@Author::ETHER/Test::ReportPrereqs'
+      version: '0.028'
+    -
+      class: Dist::Zilla::Plugin::Test::Portability
+      config:
+        Dist::Zilla::Plugin::Test::Portability:
+          options: ''
+      name: '@Author::ETHER/Test::Portability'
+      version: '2.001000'
+    -
+      class: Dist::Zilla::Plugin::Git::Describe
+      name: '@Author::ETHER/Git::Describe'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::SurgicalPodWeaver
+      config:
+        Dist::Zilla::Plugin::PodWeaver:
+          config_plugins:
+            - '@Author::ETHER'
+          finder:
+            - ':InstallModules'
+            - ':ExecFiles'
+          plugins:
+            -
+              class: Pod::Weaver::Plugin::EnsurePod5
+              name: '@Author::ETHER/EnsurePod5'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Plugin::H1Nester
+              name: '@Author::ETHER/H1Nester'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Plugin::SingleEncoding
+              name: '@Author::ETHER/SingleEncoding'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Plugin::Transformer
+              name: '@Author::ETHER/List'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Plugin::Transformer
+              name: '@Author::ETHER/Verbatim'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Region
+              name: '@Author::ETHER/header'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Name
+              name: '@Author::ETHER/Name'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Version
+              name: '@Author::ETHER/Version'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Region
+              name: '@Author::ETHER/prelude'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Generic
+              name: SYNOPSIS
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Generic
+              name: DESCRIPTION
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Generic
+              name: OVERVIEW
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Collect
+              name: ATTRIBUTES
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Collect
+              name: METHODS
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Collect
+              name: FUNCTIONS
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Collect
+              name: TYPES
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Leftovers
+              name: '@Author::ETHER/Leftovers'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Region
+              name: '@Author::ETHER/postlude'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::GenerateSection
+              name: '@Author::ETHER/generate SUPPORT'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Authors
+              name: '@Author::ETHER/Authors'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::AllowOverride
+              name: '@Author::ETHER/allow override AUTHOR'
+              version: '0.05'
+            -
+              class: Pod::Weaver::Section::Contributors
+              name: '@Author::ETHER/Contributors'
+              version: '0.009'
+            -
+              class: Pod::Weaver::Section::Legal
+              name: '@Author::ETHER/Legal'
+              version: '4.018'
+            -
+              class: Pod::Weaver::Section::Region
+              name: '@Author::ETHER/footer'
+              version: '4.018'
+      name: '@Author::ETHER/SurgicalPodWeaver'
+      version: '0.0023'
+    -
+      class: Dist::Zilla::Plugin::GithubMeta
+      name: '@Author::ETHER/GithubMeta'
+      version: '0.58'
+    -
+      class: Dist::Zilla::Plugin::AutoMetaResources
+      name: '@Author::ETHER/AutoMetaResources'
+      version: '1.21'
+    -
+      class: Dist::Zilla::Plugin::Authority
+      name: '@Author::ETHER/Authority'
+      version: '1.009'
+    -
+      class: Dist::Zilla::Plugin::MetaNoIndex
+      name: '@Author::ETHER/MetaNoIndex'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::MetaProvides::Package
+      config:
+        Dist::Zilla::Plugin::MetaProvides::Package:
+          finder:
+            - ':InstallModules'
+          finder_objects:
+            -
+              class: Dist::Zilla::Plugin::FinderCode
+              name: ':InstallModules'
+              version: '6.024'
+          include_underscores: 0
+        Dist::Zilla::Role::MetaProvider::Provider:
+          $Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004'
+          inherit_missing: '0'
+          inherit_version: '0'
+          meta_noindex: '1'
+        Dist::Zilla::Role::ModuleMetadata:
+          Module::Metadata: '1.000037'
+          version: '0.006'
+      name: '@Author::ETHER/MetaProvides::Package'
+      version: '2.004003'
+    -
+      class: Dist::Zilla::Plugin::MetaConfig
+      name: '@Author::ETHER/MetaConfig'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Keywords
+      config:
+        Dist::Zilla::Plugin::Keywords:
+          keywords: []
+      name: '@Author::ETHER/Keywords'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::UseUnsafeInc
+      config:
+        Dist::Zilla::Plugin::UseUnsafeInc:
+          dot_in_INC: 0
+      name: '@Author::ETHER/UseUnsafeInc'
+      version: '0.001'
+    -
+      class: Dist::Zilla::Plugin::AutoPrereqs
+      name: '@Author::ETHER/AutoPrereqs'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Prereqs::AuthorDeps
+      name: '@Author::ETHER/Prereqs::AuthorDeps'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::MinimumPerl
+      name: '@Author::ETHER/MinimumPerl'
+      version: '1.006'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: x_Dist_Zilla
+          type: requires
+      name: '@Author::ETHER/pod_weaving'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::MakeMaker
+      config:
+        Dist::Zilla::Role::TestRunner:
+          default_jobs: 9
+      name: '@Author::ETHER/MakeMaker'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Git::Contributors
+      config:
+        Dist::Zilla::Plugin::Git::Contributors:
+          git_version: 2.34.1
+          include_authors: 0
+          include_releaser: 1
+          order_by: commits
+          paths: []
+      name: '@Author::ETHER/Git::Contributors'
+      version: '0.036'
+    -
+      class: Dist::Zilla::Plugin::StaticInstall
+      config:
+        Dist::Zilla::Plugin::StaticInstall:
+          dry_run: 1
+          mode: auto
+      name: '@Author::ETHER/StaticInstall'
+      version: '0.012'
+    -
+      class: Dist::Zilla::Plugin::RunExtraTests
+      config:
+        Dist::Zilla::Role::TestRunner:
+          default_jobs: 9
+      name: '@Author::ETHER/RunExtraTests'
+      version: '0.029'
+    -
+      class: Dist::Zilla::Plugin::CheckSelfDependency
+      config:
+        Dist::Zilla::Plugin::CheckSelfDependency:
+          finder:
+            - ':InstallModules'
+        Dist::Zilla::Role::ModuleMetadata:
+          Module::Metadata: '1.000037'
+          version: '0.006'
+      name: '@Author::ETHER/CheckSelfDependency'
+      version: '0.011'
+    -
+      class: Dist::Zilla::Plugin::Run::AfterBuild
+      config:
+        Dist::Zilla::Plugin::Run::Role::Runner:
+          fatal_errors: 1
+          quiet: 1
+          run:
+            - "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\""
+          version: '0.048'
+      name: '@Author::ETHER/.ackrc'
+      version: '0.048'
+    -
+      class: Dist::Zilla::Plugin::Run::AfterBuild
+      config:
+        Dist::Zilla::Plugin::Run::Role::Runner:
+          eval:
+            - "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }"
+          fatal_errors: 0
+          quiet: 1
+          version: '0.048'
+      name: '@Author::ETHER/.latest'
+      version: '0.048'
+    -
+      class: Dist::Zilla::Plugin::CheckStrictVersion
+      name: '@Author::ETHER/CheckStrictVersion'
+      version: '0.001'
+    -
+      class: Dist::Zilla::Plugin::CheckMetaResources
+      name: '@Author::ETHER/CheckMetaResources'
+      version: '0.001'
+    -
+      class: Dist::Zilla::Plugin::EnsureLatestPerl
+      config:
+        Dist::Zilla::Plugin::EnsureLatestPerl:
+          Module::CoreList: '5.20220120'
+      name: '@Author::ETHER/EnsureLatestPerl'
+      version: '0.008'
+    -
+      class: Dist::Zilla::Plugin::PromptIfStale
+      config:
+        Dist::Zilla::Plugin::PromptIfStale:
+          check_all_plugins: 1
+          check_all_prereqs: 1
+          modules: []
+          phase: release
+          run_under_travis: 0
+          skip: []
+      name: '@Author::ETHER/stale modules, release'
+      version: '0.057'
+    -
+      class: Dist::Zilla::Plugin::Git::Check
+      config:
+        Dist::Zilla::Plugin::Git::Check:
+          untracked_files: die
+        Dist::Zilla::Role::Git::DirtyFiles:
+          allow_dirty: []
+          allow_dirty_match: []
+          changelog: Changes
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+      name: '@Author::ETHER/initial check'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts
+      config:
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+      name: '@Author::ETHER/Git::CheckFor::MergeConflicts'
+      version: '0.014'
+    -
+      class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch
+      config:
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+      name: '@Author::ETHER/Git::CheckFor::CorrectBranch'
+      version: '0.014'
+    -
+      class: Dist::Zilla::Plugin::Git::Remote::Check
+      name: '@Author::ETHER/Git::Remote::Check'
+      version: 0.1.2
+    -
+      class: Dist::Zilla::Plugin::CheckPrereqsIndexed
+      name: '@Author::ETHER/CheckPrereqsIndexed'
+      version: '0.021'
+    -
+      class: Dist::Zilla::Plugin::TestRelease
+      name: '@Author::ETHER/TestRelease'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Git::Check
+      config:
+        Dist::Zilla::Plugin::Git::Check:
+          untracked_files: die
+        Dist::Zilla::Role::Git::DirtyFiles:
+          allow_dirty: []
+          allow_dirty_match: []
+          changelog: Changes
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+      name: '@Author::ETHER/after tests'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::CheckIssues
+      name: '@Author::ETHER/CheckIssues'
+      version: '0.011'
+    -
+      class: Dist::Zilla::Plugin::UploadToCPAN
+      name: '@Author::ETHER/UploadToCPAN'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::CopyFilesFromRelease
+      config:
+        Dist::Zilla::Plugin::CopyFilesFromRelease:
+          filename:
+            - CONTRIBUTING
+            - INSTALL
+            - LICENCE
+            - LICENSE
+            - ppport.h
+          match: []
+      name: '@Author::ETHER/copy generated files'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::ReadmeAnyFromPod
+      config:
+        Dist::Zilla::Role::FileWatcher:
+          version: '0.006'
+      name: '@Author::ETHER/ReadmeAnyFromPod'
+      version: '0.163250'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: develop
+          type: recommends
+      name: '@Author::ETHER/@Git::VersionManager/pluginbundle version'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::RewriteVersion::Transitional
+      config:
+        Dist::Zilla::Plugin::RewriteVersion:
+          add_tarball_name: 0
+          finders:
+            - ':ExecFiles'
+            - ':InstallModules'
+          global: 1
+          skip_version_provider: 0
+        Dist::Zilla::Plugin::RewriteVersion::Transitional: {}
+      name: '@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional'
+      version: '0.009'
+    -
+      class: Dist::Zilla::Plugin::MetaProvides::Update
+      name: '@Author::ETHER/@Git::VersionManager/MetaProvides::Update'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::CopyFilesFromRelease
+      config:
+        Dist::Zilla::Plugin::CopyFilesFromRelease:
+          filename:
+            - Changes
+          match: []
+      name: '@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease'
+      version: '0.007'
+    -
+      class: Dist::Zilla::Plugin::Git::Commit
+      config:
+        Dist::Zilla::Plugin::Git::Commit:
+          add_files_in:
+            - .
+          commit_msg: '%N-%v%t%n%n%c'
+          signoff: 0
+        Dist::Zilla::Role::Git::DirtyFiles:
+          allow_dirty:
+            - CONTRIBUTING
+            - Changes
+            - INSTALL
+            - LICENSE
+            - README.pod
+          allow_dirty_match: []
+          changelog: Changes
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+        Dist::Zilla::Role::Git::StringFormatter:
+          time_zone: local
+      name: '@Author::ETHER/@Git::VersionManager/release snapshot'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::Git::Tag
+      config:
+        Dist::Zilla::Plugin::Git::Tag:
+          branch: ~
+          changelog: Changes
+          signed: 0
+          tag: v0.40
+          tag_format: v%V
+          tag_message: v%v%t
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+        Dist::Zilla::Role::Git::StringFormatter:
+          time_zone: local
+      name: '@Author::ETHER/@Git::VersionManager/Git::Tag'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional
+      config:
+        Dist::Zilla::Plugin::BumpVersionAfterRelease:
+          finders:
+            - ':InstallModules'
+          global: 1
+          munge_makefile_pl: 1
+        Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional: {}
+      name: '@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional'
+      version: '0.009'
+    -
+      class: Dist::Zilla::Plugin::NextRelease
+      name: '@Author::ETHER/@Git::VersionManager/NextRelease'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Git::Commit
+      config:
+        Dist::Zilla::Plugin::Git::Commit:
+          add_files_in: []
+          commit_msg: 'increment $VERSION after %v release'
+          signoff: 0
+        Dist::Zilla::Role::Git::DirtyFiles:
+          allow_dirty:
+            - Build.PL
+            - Changes
+            - Makefile.PL
+          allow_dirty_match:
+            - (?^:^lib/.*\.pm$)
+          changelog: Changes
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+        Dist::Zilla::Role::Git::StringFormatter:
+          time_zone: local
+      name: '@Author::ETHER/@Git::VersionManager/post-release commit'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: x_Dist_Zilla
+          type: requires
+      name: '@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Git::Push
+      config:
+        Dist::Zilla::Plugin::Git::Push:
+          push_to:
+            - origin
+          remotes_must_exist: 1
+        Dist::Zilla::Role::Git::Repo:
+          git_version: 2.34.1
+          repo_root: .
+      name: '@Author::ETHER/Git::Push'
+      version: '2.048'
+    -
+      class: Dist::Zilla::Plugin::GitHub::Update
+      config:
+        Dist::Zilla::Plugin::GitHub::Update:
+          metacpan: 1
+      name: '@Author::ETHER/GitHub::Update'
+      version: '0.48'
+    -
+      class: Dist::Zilla::Plugin::Run::AfterRelease
+      config:
+        Dist::Zilla::Plugin::Run::Role::Runner:
+          fatal_errors: 0
+          quiet: 0
+          run:
+            - REDACTED
+          version: '0.048'
+      name: '@Author::ETHER/install release'
+      version: '0.048'
+    -
+      class: Dist::Zilla::Plugin::Run::AfterRelease
+      config:
+        Dist::Zilla::Plugin::Run::Role::Runner:
+          eval:
+            - 'print "release complete!\xa"'
+          fatal_errors: 1
+          quiet: 1
+          version: '0.048'
+      name: '@Author::ETHER/release complete'
+      version: '0.048'
+    -
+      class: Dist::Zilla::Plugin::ConfirmRelease
+      name: '@Author::ETHER/ConfirmRelease'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: x_Dist_Zilla
+          type: requires
+      name: '@Author::ETHER/prereqs for @Author::ETHER'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::DynamicPrereqs
+      config:
+        Dist::Zilla::Role::ModuleMetadata:
+          Module::Metadata: '1.000037'
+          version: '0.006'
+      name: DynamicPrereqs
+      version: '0.039'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: test
+          type: requires
+      name: TestRequires
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: runtime
+          type: recommends
+      name: RuntimeRecommends
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Prereqs
+      config:
+        Dist::Zilla::Plugin::Prereqs:
+          phase: develop
+          type: requires
+      name: DevelopRequires
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::Prereqs::Soften
+      config:
+        Dist::Zilla::Plugin::Prereqs::Soften:
+          copy_to:
+            - develop.requires
+          modules:
+            - Variable::Magic
+          modules_from_features: ~
+          to_relationship: suggests
+      name: Prereqs::Soften
+      version: '0.006003'
+    -
+      class: Dist::Zilla::Plugin::Test::CheckBreaks
+      config:
+        Dist::Zilla::Plugin::Test::CheckBreaks:
+          conflicts_module:
+            - Module::Runtime::Conflicts
+            - Moose::Conflicts
+            - Package::Stash::Conflicts
+          no_forced_deps: 0
+        Dist::Zilla::Role::ModuleMetadata:
+          Module::Metadata: '1.000037'
+          version: '0.006'
+      name: Test::CheckBreaks
+      version: '0.019'
+    -
+      class: Dist::Zilla::Plugin::Conflicts
+      name: Conflicts
+      version: '0.19'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':InstallModules'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':IncModules'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':TestFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':ExtraTestFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':ExecFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':PerlExecFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':ShareFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':MainModule'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':AllFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::FinderCode
+      name: ':NoFiles'
+      version: '6.024'
+    -
+      class: Dist::Zilla::Plugin::VerifyPhases
+      name: '@Author::ETHER/PHASE VERIFICATION'
+      version: '0.016'
+  zilla:
+    class: Dist::Zilla::Dist::Builder
+    config:
+      is_trial: '0'
+    version: '6.024'
+x_authority: cpan:STEVAN
+x_breaks:
+  Class::MOP: '<= 1.08'
+  MooseX::Method::Signatures: '<= 0.36'
+  MooseX::Role::WithOverloading: '<= 0.08'
+  namespace::clean: '<= 0.18'
+x_contributors:
+  - 'Karen Etheridge <ether@cpan.org>'
+  - 'Carlos Lima <carlos@multi>'
+  - 'Christian Walde <walde.christian@googlemail.com>'
+  - 'Dave Rolsky <autarch@urth.org>'
+  - 'Justin Hunter <justin.d.hunter@gmail.com>'
+  - 'Kent Fredric <kentfredric@gmail.com>'
+  - 'Niko Tyni <ntyni@debian.org>'
+  - 'Renee <reb@perl-services.de>'
+  - 'Tim Bunce <Tim.Bunce@pobox.com>'
+x_generated_by_perl: v5.35.8
+x_serialization_backend: 'YAML::Tiny version 1.73'
+x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later'
+x_use_unsafe_inc: 0
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..047805d
--- /dev/null
@@ -0,0 +1,176 @@
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.024.
+use strict;
+use warnings;
+
+use 5.008001;
+
+use ExtUtils::MakeMaker;
+check_conflicts();
+
+my %WriteMakefileArgs = (
+  "ABSTRACT" => "Routines for manipulating stashes",
+  "AUTHOR" => "Stevan Little <stevan.little\@iinteractive.com>, Jesse Luehrs <doy\@tozt.net>",
+  "CONFIGURE_REQUIRES" => {
+    "Dist::CheckConflicts" => "0.02",
+    "ExtUtils::MakeMaker" => 0,
+    "Text::ParseWords" => 0
+  },
+  "DISTNAME" => "Package-Stash",
+  "EXE_FILES" => [
+    "bin/package-stash-conflicts"
+  ],
+  "LICENSE" => "perl",
+  "MIN_PERL_VERSION" => "5.008001",
+  "NAME" => "Package::Stash",
+  "PREREQ_PM" => {
+    "B" => 0,
+    "Carp" => 0,
+    "Dist::CheckConflicts" => "0.02",
+    "Getopt::Long" => 0,
+    "Module::Implementation" => "0.06",
+    "Scalar::Util" => 0,
+    "Symbol" => 0,
+    "constant" => 0,
+    "strict" => 0,
+    "warnings" => 0
+  },
+  "TEST_REQUIRES" => {
+    "CPAN::Meta::Check" => "0.011",
+    "CPAN::Meta::Requirements" => 0,
+    "ExtUtils::MakeMaker" => 0,
+    "File::Spec" => 0,
+    "Test::Fatal" => 0,
+    "Test::More" => "0.88",
+    "Test::Needs" => 0,
+    "base" => 0,
+    "lib" => 0
+  },
+  "VERSION" => "0.40",
+  "test" => {
+    "TESTS" => "t/*.t t/impl-selection/*.t"
+  }
+);
+
+
+my %FallbackPrereqs = (
+  "B" => 0,
+  "CPAN::Meta::Check" => "0.011",
+  "CPAN::Meta::Requirements" => 0,
+  "Carp" => 0,
+  "Dist::CheckConflicts" => "0.02",
+  "ExtUtils::MakeMaker" => 0,
+  "File::Spec" => 0,
+  "Getopt::Long" => 0,
+  "Module::Implementation" => "0.06",
+  "Scalar::Util" => 0,
+  "Symbol" => 0,
+  "Test::Fatal" => 0,
+  "Test::More" => "0.88",
+  "Test::Needs" => 0,
+  "base" => 0,
+  "constant" => 0,
+  "lib" => 0,
+  "strict" => 0,
+  "warnings" => 0
+);
+
+# inserted by Dist::Zilla::Plugin::DynamicPrereqs 0.039
+requires('Package::Stash::XS', '0.26') if !want_pp() and can_xs();
+
+
+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);
+
+# inserted by Dist::Zilla::Plugin::DynamicPrereqs 0.039
+sub _add_prereq {
+  my ($mm_key, $module, $version_or_range) = @_;
+  $version_or_range ||= 0;
+  warn "$module already exists in $mm_key (at version $WriteMakefileArgs{$mm_key}{$module}) -- need to do a sane metamerge!"
+    if exists $WriteMakefileArgs{$mm_key}{$module}
+      and $WriteMakefileArgs{$mm_key}{$module} ne '0'
+      and $WriteMakefileArgs{$mm_key}{$module} ne $version_or_range;
+  warn "$module already exists in FallbackPrereqs (at version $FallbackPrereqs{$module}) -- need to do a sane metamerge!"
+    if exists $FallbackPrereqs{$module} and $FallbackPrereqs{$module} ne '0'
+        and $FallbackPrereqs{$module} ne $version_or_range;
+  $WriteMakefileArgs{$mm_key}{$module} = $FallbackPrereqs{$module} = $version_or_range;
+  return;
+}
+
+use lib 'inc';
+use ExtUtils::HasCompiler 0.014 'can_compile_loadable_object';
+{
+  my $can_xs;
+  sub can_xs {
+    return $can_xs if defined $can_xs;
+    $can_xs = can_compile_loadable_object(quiet => 1) ? 1 : 0;
+  }
+}
+
+{
+  my $parsed_args;
+  sub parse_args {
+    return $parsed_args if defined $parsed_args;
+    require ExtUtils::MakeMaker;
+    require Text::ParseWords;
+    ExtUtils::MakeMaker::parse_args(
+      my $tmp = {},
+      Text::ParseWords::shellwords($ENV{PERL_MM_OPT} || ''),
+      @ARGV,
+    );
+    $parsed_args = $tmp->{ARGS} || {};
+  }
+}
+
+sub requires { goto &runtime_requires }
+
+sub runtime_requires {
+  my ($module, $version_or_range) = @_;
+  _add_prereq(PREREQ_PM => $module, $version_or_range);
+}
+
+{
+  my $want_pp;
+  sub want_pp {
+    return $want_pp if defined $want_pp;
+    $want_pp = parse_args()->{PUREPERL_ONLY} ? 1 : 0
+  }
+}
+
+sub check_conflicts {
+    if ( eval { require './lib/Package/Stash/Conflicts.pm'; 1; } ) {
+        if ( eval { Package::Stash::Conflicts->check_conflicts; 1 } ) {
+            return;
+        }
+        else {
+            my $err = $@;
+            $err =~ s/^/    /mg;
+            warn "***\n$err***\n";
+        }
+    }
+    else {
+        print <<'EOF';
+***
+    Your toolchain doesn't support configure_requires, so Dist::CheckConflicts
+    hasn't been installed yet. You should check for conflicting modules
+    manually using the 'package-stash-conflicts' script that is installed with
+    this distribution once the installation finishes.
+***
+EOF
+    }
+
+    return if $ENV{AUTOMATED_TESTING} || $ENV{NONINTERACTIVE_TESTING};
+
+    # More or less copied from Module::Build
+    return if $ENV{PERL_MM_USE_DEFAULT};
+    return unless -t STDIN && ( -t STDOUT || !( -f STDOUT || -c STDOUT ) );
+
+    sleep 4;
+}
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..97f47fa
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+This archive contains the distribution Package-Stash,
+version 0.40:
+
+  Routines for manipulating stashes
+
+This software is copyright (c) 2022 by Jesse Luehrs.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+
+This README file was generated by Dist::Zilla::Plugin::Readme v6.024.
diff --git a/bin/package-stash-conflicts b/bin/package-stash-conflicts
new file mode 100644 (file)
index 0000000..783a88b
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+# PODNAME: package-stash-conflicts
+
+# this script was generated with Dist::Zilla::Plugin::Conflicts 0.19
+
+use Getopt::Long;
+use Package::Stash::Conflicts;
+
+my $verbose;
+GetOptions( 'verbose|v' => \$verbose );
+
+if ($verbose) {
+    Package::Stash::Conflicts->check_conflicts;
+}
+else {
+    my @conflicts = Package::Stash::Conflicts->calculate_conflicts;
+    print "$_\n" for map { $_->{package} } @conflicts;
+    exit @conflicts;
+}
diff --git a/dist.ini b/dist.ini
new file mode 100644 (file)
index 0000000..3a14ee6
--- /dev/null
+++ b/dist.ini
@@ -0,0 +1,53 @@
+name = Package-Stash
+author = Stevan Little <stevan.little@iinteractive.com>
+author = Jesse Luehrs <doy@tozt.net>
+license = Perl_5
+copyright_holder = Jesse Luehrs
+
+[@Author::ETHER]
+:version = 0.119
+authority = cpan:STEVAN
+installer = MakeMaker
+ExecDir.dir = bin
+surgical_podweaver = 1
+; there's some bug with PodCoverageTests and Conflicts on travis that i can't
+; seem to track down, so just disable that for now
+-remove = PodCoverageTests
+-remove = Test::CleanNamespaces
+Test::MinimumVersion.max_target_perl = 5.008001
+
+; authordep ExtUtils::HasCompiler = 0.014
+[DynamicPrereqs]
+:version = 0.029
+-body = requires('Package::Stash::XS', '0.26') if !want_pp() and can_xs();
+
+; authordep Dist::Zilla::Plugin::SurgicalPodWeaver
+
+[Prereqs / TestRequires]
+Test::More = 0.88
+
+[Prereqs / RuntimeRecommends]
+; XXX keep this in sync with dynamic prereq above
+Package::Stash::XS = 0.26
+
+[Prereqs / DevelopRequires]
+Test::LeakTrace = 0
+Package::Anon = 0
+
+[Prereqs::Soften]
+module = Variable::Magic
+copy_to = develop.requires
+to_relationship = suggests
+
+[Test::CheckBreaks]
+conflicts_module = Package::Stash::Conflicts
+conflicts_module = Moose::Conflicts
+conflicts_module = Module::Runtime::Conflicts
+
+; this must be last, after all prereqs have been declared
+[Conflicts]
+-script = bin/package-stash-conflicts
+Class::MOP                    = 1.08
+MooseX::Role::WithOverloading = 0.08
+namespace::clean              = 0.18
+MooseX::Method::Signatures    = 0.36
diff --git a/inc/ExtUtils/HasCompiler.pm b/inc/ExtUtils/HasCompiler.pm
new file mode 100644 (file)
index 0000000..dff6c24
--- /dev/null
@@ -0,0 +1,308 @@
+package ExtUtils::HasCompiler;
+$ExtUtils::HasCompiler::VERSION = '0.023';
+use strict;
+use warnings;
+
+use base 'Exporter';
+our @EXPORT_OK = qw/can_compile_loadable_object can_compile_static_library can_compile_extension/;
+our %EXPORT_TAGS = (all => \@EXPORT_OK);
+
+use Config;
+use Carp 'carp';
+use File::Basename 'basename';
+use File::Spec::Functions qw/catfile catdir rel2abs/;
+use File::Temp qw/tempdir tempfile/;
+
+my $tempdir = tempdir('HASCOMPILERXXXX', CLEANUP => 1, DIR => '.');
+
+my $loadable_object_format = <<'END';
+#define PERL_NO_GET_CONTEXT
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifndef PERL_UNUSED_VAR
+#define PERL_UNUSED_VAR(var)
+#endif
+
+XS(exported) {
+#ifdef dVAR
+       dVAR;
+#endif
+       dXSARGS;
+
+       PERL_UNUSED_VAR(cv); /* -W */
+       PERL_UNUSED_VAR(items); /* -W */
+
+       XSRETURN_IV(42);
+}
+
+#ifndef XS_EXTERNAL
+#define XS_EXTERNAL(foo) XS(foo)
+#endif
+
+/* we don't want to mess with .def files on mingw */
+#if defined(WIN32) && defined(__GNUC__)
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
+EXPORT XS_EXTERNAL(boot_%s) {
+#ifdef dVAR
+       dVAR;
+#endif
+       dXSARGS;
+
+       PERL_UNUSED_VAR(cv); /* -W */
+       PERL_UNUSED_VAR(items); /* -W */
+
+       newXS("%s::exported", exported, __FILE__);
+}
+
+END
+
+my $counter = 1;
+my %prelinking = map { $_ => 1 } qw/MSWin32 VMS aix/;
+
+sub can_compile_loadable_object {
+       my %args = @_;
+
+       my $output = $args{output} || \*STDOUT;
+
+       my $config = $args{config} || 'ExtUtils::HasCompiler::Config';
+       return if not $config->get('usedl');
+
+       my ($source_handle, $source_name) = tempfile('TESTXXXX', DIR => $tempdir, SUFFIX => '.c', UNLINK => 1);
+       my $basename = basename($source_name, '.c');
+       my $abs_basename = catfile($tempdir, $basename);
+
+       my ($cc, $ccflags, $optimize, $cccdlflags, $ld, $ldflags, $lddlflags, $libperl, $perllibs, $archlibexp, $_o, $dlext) = map { $config->get($_) } qw/cc ccflags optimize cccdlflags ld ldflags lddlflags libperl perllibs archlibexp _o dlext/;
+
+       my $incdir = catdir($archlibexp, 'CORE');
+       my $object_file = $abs_basename.$_o;
+       my $loadable_object = "$abs_basename.$dlext";
+
+       my @commands;
+       if ($^O eq 'MSWin32' && $cc =~ /^cl/) {
+               push @commands, qq{$cc $ccflags $cccdlflags $optimize /I "$incdir" /c $source_name /Fo$object_file};
+               push @commands, qq{$ld $object_file $lddlflags $libperl $perllibs /out:$loadable_object /def:$abs_basename.def /pdb:$abs_basename.pdb};
+       }
+       elsif ($^O eq 'VMS') {
+               # Mksymlists is only the beginning of the story.
+               open my $opt_fh, '>>', "$abs_basename.opt" or do { carp "Couldn't append to '$abs_basename.opt'"; return };
+               print $opt_fh "PerlShr/Share\n";
+               close $opt_fh;
+
+               my $incdirs = $ccflags =~ s{ /inc[^=]+ (?:=)+ (?:\()? ( [^\/\)]* ) }{}xi ? "$1,$incdir" : $incdir;
+               push @commands, qq{$cc $ccflags $optimize /include=($incdirs) $cccdlflags $source_name /obj=$object_file};
+               push @commands, qq{$ld $ldflags $lddlflags=$loadable_object $object_file,$abs_basename.opt/OPTIONS,${incdir}perlshr_attr.opt/OPTIONS' $perllibs};
+       }
+       else {
+               my @extra;
+               my $inc = qq{"-I$incdir"};
+               if ($^O eq 'MSWin32') {
+                       my $lib = '-l' . ($libperl =~ /lib([^.]+)\./)[0];
+                       push @extra, "$abs_basename.def", $lib, $perllibs;
+               }
+               elsif ($^O =~ /^(cygwin|msys)$/) {
+                       push @extra, catfile($incdir, $config->get('useshrplib') ? 'libperl.dll.a' : 'libperl.a');
+               }
+               elsif ($^O eq 'aix') {
+                       $lddlflags =~ s/\Q$(BASEEXT)\E/$abs_basename/;
+                       $lddlflags =~ s/\Q$(PERL_INC)\E/$incdir/;
+               }
+               elsif ($^O eq 'android') {
+                       push @extra, qq{"-L$incdir"}, '-lperl', $perllibs;
+               }
+               elsif ($^O eq 'darwin' && $config->get('perlpath') eq '/usr/bin/perl' && ($config->get('osvers') =~ /(\d+)/)[0] >= 18) {
+                       $inc = qq{-iwithsysroot "$incdir"};
+               }
+               push @commands, qq{$cc $ccflags $optimize $inc $cccdlflags -c $source_name -o $object_file};
+               push @commands, qq{$ld $object_file -o $loadable_object $lddlflags @extra};
+       }
+
+       if ($prelinking{$^O}) {
+               require ExtUtils::Mksymlists;
+               ExtUtils::Mksymlists::Mksymlists(NAME => $basename, FILE => $abs_basename, IMPORTS => {});
+       }
+
+       my $shortname = '_Loadable' . $counter++;
+       my $package = "ExtUtils::HasCompiler::$shortname";
+       printf $source_handle $loadable_object_format, $basename, $package or do { carp "Couldn't write to $source_name: $!"; return };
+       close $source_handle or do { carp "Couldn't close $source_name: $!"; return };
+
+       for my $command (@commands) {
+               print $output "$command\n" if not $args{quiet};
+               system $command and do { carp "Couldn't execute $command: $!"; return };
+       }
+
+       # Skip loading when cross-compiling
+       return 1 if exists $args{skip_load} ? $args{skip_load} : $config->get('usecrosscompile');
+
+       require DynaLoader;
+       local @DynaLoader::dl_require_symbols = "boot_$basename";
+       my $handle = DynaLoader::dl_load_file(rel2abs($loadable_object), 0);
+       if ($handle) {
+               my $symbol = DynaLoader::dl_find_symbol($handle, "boot_$basename") or do { carp "Couldn't find boot symbol for $basename"; return };
+               my $compilet = DynaLoader::dl_install_xsub('__ANON__::__ANON__', $symbol, $source_name);
+               my $ret = eval { $compilet->(); $package->exported } or carp $@;
+               delete $ExtUtils::HasCompiler::{"$shortname\::"};
+               eval { DynaLoader::dl_unload_file($handle) } or carp $@;
+               return defined $ret && $ret == 42;
+       }
+       else {
+               carp "Couldn't load $loadable_object: " . DynaLoader::dl_error();
+               return;
+       }
+}
+
+my %static_unsupported_on = map { $_ => 1 } qw/VMS aix MSWin32 cygwin/;
+sub can_compile_static_library {
+       my %args = @_;
+
+       my $output = $args{output} || \*STDOUT;
+
+       my $config = $args{config} || 'ExtUtils::HasCompiler::Config';
+       return if $config->get('useshrplib') eq 'true';
+
+       my ($source_handle, $source_name) = tempfile('TESTXXXX', DIR => $tempdir, SUFFIX => '.c', UNLINK => 1);
+       my $basename = basename($source_name, '.c');
+       my $abs_basename = catfile($tempdir, $basename);
+
+       my ($cc, $ccflags, $optimize, $ar, $full_ar, $ranlib, $archlibexp, $_o, $lib_ext) = map { $config->get($_) } qw/cc ccflags optimize ar full_ar ranlib archlibexp _o lib_ext/;
+       my $incdir = catdir($archlibexp, 'CORE');
+       my $object_file = "$abs_basename$_o";
+       my $static_library = $abs_basename.$lib_ext;
+
+       my @commands;
+       if ($static_unsupported_on{$^O}) {
+               return;
+       }
+       else {
+               my $my_ar = length $full_ar ? $full_ar : $ar;
+               push @commands, qq{$cc $ccflags $optimize "-I$incdir" -c $source_name -o $object_file};
+               push @commands, qq{$my_ar cr $static_library $object_file};
+               push @commands, qq{$ranlib $static_library} if $ranlib ne ':';
+       }
+
+       my $shortname = '_Loadable' . $counter++;
+       my $package = "ExtUtils::HasCompiler::$shortname";
+       printf $source_handle $loadable_object_format, $basename, $package or do { carp "Couldn't write to $source_name: $!"; return };
+       close $source_handle or do { carp "Couldn't close $source_name: $!"; return };
+
+       for my $command (@commands) {
+               print $output "$command\n" if not $args{quiet};
+               system $command and do { carp "Couldn't execute $command: $!"; return };
+       }
+       return 1;
+}
+
+sub can_compile_extension {
+       my %args = @_;
+       $args{config} ||= 'ExtUtils::HasCompiler::Config';
+       my $linktype = $args{linktype} || ($args{config}->get('usedl') ? 'dynamic' : 'static');
+       return $linktype eq 'static' ? can_compile_static_library(%args) : can_compile_loadable_object(%args);
+}
+
+sub ExtUtils::HasCompiler::Config::get {
+       my (undef, $key) = @_;
+       return $ENV{uc $key} || $Config{$key};
+}
+
+1;
+
+# ABSTRACT: Check for the presence of a compiler
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+ExtUtils::HasCompiler - Check for the presence of a compiler
+
+=head1 VERSION
+
+version 0.023
+
+=head1 SYNOPSIS
+
+ use ExtUtils::HasCompiler 'can_compile_extension';
+ if (can_compile_extension()) {
+   ...
+ }
+ else {
+   ...
+ }
+
+=head1 DESCRIPTION
+
+This module tries to check if the current system is capable of compiling, linking and loading an XS module.
+
+B<Notice>: this is an early release, interface stability isn't guaranteed yet.
+
+=head1 FUNCTIONS
+
+=head2 can_compile_loadable_object(%opts)
+
+This checks if the system can compile, link and load a perl loadable object. It may take the following options:
+
+=over 4
+
+=item * quiet
+
+Do not output the executed compilation commands.
+
+=item * config
+
+An L<ExtUtils::Config|ExtUtils::Config> (compatible) object for configuration.
+
+=item * skip_load
+
+This causes can_compile_loadable_object to not try to load the generated object. This defaults to true on a cross-compiling perl.
+
+=back
+
+=head2 can_compile_static_library(%opts)
+
+This checks if the system can compile and link a perl static library. It does not check it it can compile a new perl with it. It may take the following options:
+
+=over 4
+
+=item * quiet
+
+Do not output the executed compilation commands.
+
+=item * config
+
+An L<ExtUtils::Config|ExtUtils::Config> (compatible) object for configuration.
+
+=back
+
+=head2 can_compile_extension(%opts)
+
+This will call either C<can_compile_loadable_object>, or C<can_compile_static_library>, depending on which is the default on your configuration. In addition to the arguments listed above, it can take one more optional argument:
+
+=over 4
+
+=item * linktype
+
+This will force the linktype to be either static or dynamic. Dynamic compilation on a static perl won't work, but static libraries can be viable on a dynamic perl.
+
+=back
+
+=head1 AUTHOR
+
+Leon Timmermans <leont@cpan.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Leon Timmermans.
+
+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
diff --git a/lib/Package/Stash.pm b/lib/Package/Stash.pm
new file mode 100644 (file)
index 0000000..3d98809
--- /dev/null
@@ -0,0 +1,308 @@
+package Package::Stash; # git description: v0.39-2-ga9a8cce
+use strict;
+use warnings;
+use 5.008001;
+# ABSTRACT: Routines for manipulating stashes
+
+our $VERSION = '0.40';
+our $IMPLEMENTATION;
+
+use Module::Implementation 0.06;
+
+BEGIN {
+    local $ENV{PACKAGE_STASH_IMPLEMENTATION} = $IMPLEMENTATION
+      if ( $IMPLEMENTATION and not $ENV{PACKAGE_STASH_IMPLEMENTATION} );
+
+    Module::Implementation::build_loader_sub(
+        implementations => [ 'XS', 'PP' ],
+        symbols         => [qw(
+            new
+            name
+            namespace
+            add_symbol
+            remove_glob
+            has_symbol
+            get_symbol
+            get_or_add_symbol
+            remove_symbol
+            list_all_symbols
+            get_all_symbols
+        )],
+    )->();
+    $IMPLEMENTATION = Module::Implementation::implementation_for(__PACKAGE__);
+}
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Package::Stash - Routines for manipulating stashes
+
+=head1 VERSION
+
+version 0.40
+
+=head1 SYNOPSIS
+
+  my $stash = Package::Stash->new('Foo');
+  $stash->add_symbol('%foo', {bar => 1});
+  # $Foo::foo{bar} == 1
+  $stash->has_symbol('$foo') # false
+  my $namespace = $stash->namespace;
+  *{ $namespace->{foo} }{HASH} # {bar => 1}
+
+=head1 DESCRIPTION
+
+Manipulating stashes (Perl's symbol tables) is occasionally necessary, but
+incredibly messy, and easy to get wrong. This module hides all of that behind a
+simple API.
+
+NOTE: Most methods in this class require a variable specification that includes
+a sigil. If this sigil is absent, it is assumed to represent the IO slot.
+
+Due to limitations in the typeglob API available to perl code, and to typeglob
+manipulation in perl being quite slow, this module provides two
+implementations - one in pure perl, and one using XS. The XS implementation is
+to be preferred for most usages; the pure perl one is provided for cases where
+XS modules are not a possibility. The current implementation in use can be set
+by setting C<$ENV{PACKAGE_STASH_IMPLEMENTATION}> or
+C<$Package::Stash::IMPLEMENTATION> before loading Package::Stash (with the
+environment variable taking precedence), otherwise, it will use the XS
+implementation if possible, falling back to the pure perl one.
+
+=head1 METHODS
+
+=head2 new $package_name
+
+Creates a new C<Package::Stash> object, for the package given as the only
+argument.
+
+=head2 name
+
+Returns the name of the package that this object represents.
+
+=head2 namespace
+
+Returns the raw stash itself.
+
+=head2 add_symbol $variable $value %opts
+
+Adds a new package symbol, for the symbol given as C<$variable>, and optionally
+gives it an initial value of C<$value>. C<$variable> should be the name of
+variable including the sigil, so
+
+  Package::Stash->new('Foo')->add_symbol('%foo')
+
+will create C<%Foo::foo>.
+
+Valid options (all optional) are C<filename>, C<first_line_num>, and
+C<last_line_num>.
+
+C<$opts{filename}>, C<$opts{first_line_num}>, and C<$opts{last_line_num}> can
+be used to indicate where the symbol should be regarded as having been defined.
+Currently these values are only used if the symbol is a subroutine ('C<&>'
+sigil) and only if C<$^P & 0x10> is true, in which case the special C<%DB::sub>
+hash is updated to record the values of C<filename>, C<first_line_num>, and
+C<last_line_num> for the subroutine. If these are not passed, their values are
+inferred (as much as possible) from C<caller> information.
+
+=head2 remove_glob $name
+
+Removes all package variables with the given name, regardless of sigil.
+
+=head2 has_symbol $variable
+
+Returns whether or not the given package variable (including sigil) exists.
+
+=head2 get_symbol $variable
+
+Returns the value of the given package variable (including sigil).
+
+=head2 get_or_add_symbol $variable
+
+Like C<get_symbol>, except that it will return an empty hashref or
+arrayref if the variable doesn't exist.
+
+=head2 remove_symbol $variable
+
+Removes the package variable described by C<$variable> (which includes the
+sigil); other variables with the same name but different sigils will be
+untouched.
+
+=head2 list_all_symbols $type_filter
+
+Returns a list of package variable names in the package, without sigils. If a
+C<type_filter> is passed, it is used to select package variables of a given
+type, where valid types are the slots of a typeglob ('SCALAR', 'CODE', 'HASH',
+etc). Note that if the package contained any C<BEGIN> blocks, perl will leave
+an empty typeglob in the C<BEGIN> slot, so this will show up if no filter is
+used (and similarly for C<INIT>, C<END>, etc).
+
+=head2 get_all_symbols $type_filter
+
+Returns a hashref, keyed by the variable names in the package. If
+C<$type_filter> is passed, the hash will contain every variable of that type in
+the package as values, otherwise, it will contain the typeglobs corresponding
+to the variable names (basically, a clone of the stash).
+
+=for stopwords profilers
+
+This is especially useful for debuggers and profilers, which use C<%DB::sub> to
+determine where the source code for a subroutine can be found.  See
+L<http://perldoc.perl.org/perldebguts.html#Debugger-Internals> for more
+information about C<%DB::sub>.
+
+=head1 WORKING WITH VARIABLES
+
+It is important to note, that when working with scalar variables, the default
+behavior is to B<copy> values.
+
+  my $stash = Package::Stash->new('Some::Namespace');
+  my $variable = 1;
+  # $Some::Namespace::name is a copy of $variable
+  $stash->add_symbol('$name', $variable);
+  $variable++
+  # $Some::Namespace::name == 1 , $variable == 2
+
+This will likely confuse people who expect it to work the same as typeglob
+assignment, which simply creates new references to existing variables.
+
+  my $variable = 1;
+  {
+      no strict 'refs';
+      # assign $Package::Stash::name = $variable
+      *{'Package::Stash::name'} = \$variable;
+  }
+  $variable++ # affects both names
+
+If this behaviour is desired when working with Package::Stash, simply pass
+Package::Stash a scalar ref:
+
+  my $stash = Package::Stash->new('Some::Namespace');
+  my $variable = 1;
+  # $Some::Namespace::name is now $variable
+  $stash->add_symbol('$name', \$variable);
+  $variable++
+  # $Some::Namespace::name == 2 , $variable == 2
+
+This will be what you want as well if you're ever working with L<Readonly>
+variables:
+
+  use Readonly;
+  Readonly my $value, 'hello';
+
+  $stash->add_symbol('$name', \$value); # reference
+  print $Some::Namespace::name; # hello
+  # Tries to modify the read-only 'hello' and dies.
+  $Some::Namespace::name .= " world";
+
+  $stash->add_symbol('$name', $value); # copy
+  print $Some::Namespace::name; # hello
+  # No problem, modifying a copy, not the original
+  $Some::Namespace::name .= " world";
+
+=head1 BUGS / CAVEATS
+
+=over 4
+
+=item * Prior to perl 5.10, scalar slots are only considered to exist if they are defined
+
+This is due to a shortcoming within perl itself. See
+L<perlref/Making References> point 7 for more information.
+
+=item * GLOB and FORMAT variables are not (yet) accessible through this module.
+
+=item * Also, see the BUGS section for the specific backends (L<Package::Stash::XS> and L<Package::Stash::PP>)
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item * L<Class::MOP::Package>
+
+This module is a factoring out of code that used to live here
+
+=back
+
+=head1 HISTORY
+
+Based on code from L<Class::MOP::Package>, by Stevan Little and the Moose
+Cabal.
+
+=head1 SUPPORT
+
+Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Package-Stash>
+(or L<bug-Package-Stash@rt.cpan.org|mailto:bug-Package-Stash@rt.cpan.org>).
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Stevan Little <stevan.little@iinteractive.com>
+
+=item *
+
+Jesse Luehrs <doy@tozt.net>
+
+=back
+
+=head1 CONTRIBUTORS
+
+=for stopwords Karen Etheridge Carlos Lima Christian Walde Dave Rolsky Justin Hunter Kent Fredric Niko Tyni Renee Tim Bunce
+
+=over 4
+
+=item *
+
+Karen Etheridge <ether@cpan.org>
+
+=item *
+
+Carlos Lima <carlos@multi>
+
+=item *
+
+Christian Walde <walde.christian@googlemail.com>
+
+=item *
+
+Dave Rolsky <autarch@urth.org>
+
+=item *
+
+Justin Hunter <justin.d.hunter@gmail.com>
+
+=item *
+
+Kent Fredric <kentfredric@gmail.com>
+
+=item *
+
+Niko Tyni <ntyni@debian.org>
+
+=item *
+
+Renee <reb@perl-services.de>
+
+=item *
+
+Tim Bunce <Tim.Bunce@pobox.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2022 by Jesse Luehrs.
+
+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
diff --git a/lib/Package/Stash/Conflicts.pm b/lib/Package/Stash/Conflicts.pm
new file mode 100644 (file)
index 0000000..feffd45
--- /dev/null
@@ -0,0 +1,35 @@
+package # hide from PAUSE
+    Package::Stash::Conflicts;
+
+use strict;
+use warnings;
+
+# this module was generated with Dist::Zilla::Plugin::Conflicts 0.19
+
+use Dist::CheckConflicts
+    -dist      => 'Package::Stash',
+    -conflicts => {
+        'Class::MOP' => '1.08',
+        'MooseX::Method::Signatures' => '0.36',
+        'MooseX::Role::WithOverloading' => '0.08',
+        'namespace::clean' => '0.18',
+    },
+    -also => [ qw(
+        B
+        Carp
+        Dist::CheckConflicts
+        Getopt::Long
+        Module::Implementation
+        Scalar::Util
+        Symbol
+        constant
+        strict
+        warnings
+    ) ],
+
+;
+
+1;
+
+# ABSTRACT: Provide information on conflicts for Package::Stash
+# Dist::Zilla: -PodWeaver
diff --git a/lib/Package/Stash/PP.pm b/lib/Package/Stash/PP.pm
new file mode 100644 (file)
index 0000000..accd8b9
--- /dev/null
@@ -0,0 +1,503 @@
+package Package::Stash::PP;
+use strict;
+use warnings;
+# ABSTRACT: Pure perl implementation of the Package::Stash API
+
+our $VERSION = '0.40';
+
+use B;
+use Carp qw(confess);
+use Scalar::Util qw(blessed reftype weaken);
+use Symbol;
+# before 5.12, assigning to the ISA glob would make it lose its magical ->isa
+# powers
+use constant BROKEN_ISA_ASSIGNMENT => ($] < 5.012);
+# before 5.10, stashes don't ever seem to drop to a refcount of zero, so
+# weakening them isn't helpful
+use constant BROKEN_WEAK_STASH     => ($] < 5.010);
+# before 5.10, the scalar slot was always treated as existing if the
+# glob existed
+use constant BROKEN_SCALAR_INITIALIZATION => ($] < 5.010);
+# add_method on anon stashes triggers rt.perl #1804 otherwise
+# fixed in perl commit v5.13.3-70-g0fe688f
+use constant BROKEN_GLOB_ASSIGNMENT => ($] < 5.013004);
+# pre-5.10, ->isa lookups were cached in the ::ISA::CACHE:: slot
+use constant HAS_ISA_CACHE => ($] < 5.010);
+
+#pod =head1 SYNOPSIS
+#pod
+#pod   use Package::Stash;
+#pod
+#pod =head1 DESCRIPTION
+#pod
+#pod This is a backend for L<Package::Stash> implemented in pure perl, for those without a compiler or who would like to use this inline in scripts.
+#pod
+#pod =cut
+
+sub new {
+    my $class = shift;
+    my ($package) = @_;
+
+    if (!defined($package) || (ref($package) && reftype($package) ne 'HASH')) {
+        confess "Package::Stash->new must be passed the name of the "
+              . "package to access";
+    }
+    elsif (ref($package) && reftype($package) eq 'HASH') {
+        confess "The PP implementation of Package::Stash does not support "
+              . "anonymous stashes before perl 5.14"
+            if BROKEN_GLOB_ASSIGNMENT;
+
+        return bless {
+            'namespace' => $package,
+        }, $class;
+    }
+    elsif ($package =~ /\A[0-9A-Z_a-z]+(?:::[0-9A-Z_a-z]+)*\z/) {
+        return bless {
+            'package' => $package,
+        }, $class;
+    }
+    else {
+        confess "$package is not a module name";
+    }
+
+}
+
+sub name {
+    confess "Can't call name as a class method"
+        unless blessed($_[0]);
+    confess "Can't get the name of an anonymous package"
+        unless defined($_[0]->{package});
+    return $_[0]->{package};
+}
+
+sub namespace {
+    confess "Can't call namespace as a class method"
+        unless blessed($_[0]);
+
+    if (BROKEN_WEAK_STASH) {
+        no strict 'refs';
+        return \%{$_[0]->name . '::'};
+    }
+    else {
+        return $_[0]->{namespace} if defined $_[0]->{namespace};
+
+        {
+            no strict 'refs';
+            $_[0]->{namespace} = \%{$_[0]->name . '::'};
+        }
+
+        weaken($_[0]->{namespace});
+
+        return $_[0]->{namespace};
+    }
+}
+
+{
+    my %SIGIL_MAP = (
+        '$' => 'SCALAR',
+        '@' => 'ARRAY',
+        '%' => 'HASH',
+        '&' => 'CODE',
+        ''  => 'IO',
+    );
+
+    sub _deconstruct_variable_name {
+        my ($variable) = @_;
+
+        my @ret;
+        if (ref($variable) eq 'HASH') {
+            @ret = @{$variable}{qw[name sigil type]};
+        }
+        else {
+            (defined $variable && length $variable)
+                || confess "You must pass a variable name";
+
+            my $sigil = substr($variable, 0, 1, '');
+
+            if (exists $SIGIL_MAP{$sigil}) {
+                @ret = ($variable, $sigil, $SIGIL_MAP{$sigil});
+            }
+            else {
+                @ret = ("${sigil}${variable}", '', $SIGIL_MAP{''});
+            }
+        }
+
+        # XXX in pure perl, this will access things in inner packages,
+        # in xs, this will segfault - probably look more into this at
+        # some point
+        ($ret[0] !~ /::/)
+            || confess "Variable names may not contain ::";
+
+        return @ret;
+    }
+}
+
+sub _valid_for_type {
+    my ($value, $type) = @_;
+    if ($type eq 'HASH' || $type eq 'ARRAY'
+     || $type eq 'IO'   || $type eq 'CODE') {
+        return reftype($value) eq $type;
+    }
+    else {
+        my $ref = reftype($value);
+        return !defined($ref) || $ref eq 'SCALAR' || $ref eq 'REF' || $ref eq 'LVALUE' || $ref eq 'REGEXP' || $ref eq 'VSTRING';
+    }
+}
+
+sub add_symbol {
+    my ($self, $variable, $initial_value, %opts) = @_;
+
+    my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
+
+    if (@_ > 2) {
+        _valid_for_type($initial_value, $type)
+            || confess "$initial_value is not of type $type";
+
+        # cheap fail-fast check for PERLDBf_SUBLINE and '&'
+        if ($^P and $^P & 0x10 && $sigil eq '&') {
+            my $filename = $opts{filename};
+            my $first_line_num = $opts{first_line_num};
+
+            (undef, $filename, $first_line_num) = caller
+                if not defined $filename;
+
+            my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
+
+            # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
+            $DB::sub{$self->name . '::' . $name} = "$filename:$first_line_num-$last_line_num";
+        }
+    }
+
+    if (BROKEN_GLOB_ASSIGNMENT) {
+        if (@_ > 2) {
+            no strict 'refs';
+            no warnings 'redefine';
+            *{ $self->name . '::' . $name } = ref $initial_value
+                ? $initial_value : \$initial_value;
+        }
+        else {
+            no strict 'refs';
+            if (BROKEN_ISA_ASSIGNMENT && $name eq 'ISA') {
+                *{ $self->name . '::' . $name };
+            }
+            else {
+                my $undef = _undef_ref_for_type($type);
+                *{ $self->name . '::' . $name } = $undef;
+            }
+        }
+    }
+    else {
+        my $namespace = $self->namespace;
+        {
+            # using glob aliasing instead of Symbol::gensym, because otherwise,
+            # magic doesn't get applied properly.
+            # see <20120710063744.19360.qmail@lists-nntp.develooper.com> on p5p
+            local *__ANON__:: = $namespace;
+            no strict 'refs';
+            no warnings 'void';
+            no warnings 'once';
+            *{"__ANON__::$name"};
+        }
+
+        if (@_ > 2) {
+            no warnings 'redefine';
+            *{ $namespace->{$name} } = ref $initial_value
+                ? $initial_value : \$initial_value;
+        }
+        else {
+            return if BROKEN_ISA_ASSIGNMENT && $name eq 'ISA';
+            *{ $namespace->{$name} } = _undef_ref_for_type($type);
+        }
+    }
+}
+
+sub _undef_ref_for_type {
+    my ($type) = @_;
+
+    if ($type eq 'ARRAY') {
+        return [];
+    }
+    elsif ($type eq 'HASH') {
+        return {};
+    }
+    elsif ($type eq 'SCALAR') {
+        return \undef;
+    }
+    elsif ($type eq 'IO') {
+        return Symbol::geniosym;
+    }
+    elsif ($type eq 'CODE') {
+        confess "Don't know how to vivify CODE variables";
+    }
+    else {
+        confess "Unknown type $type in vivication";
+    }
+}
+
+sub remove_glob {
+    my ($self, $name) = @_;
+    delete $self->namespace->{$name};
+}
+
+sub has_symbol {
+    my ($self, $variable) = @_;
+
+    my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
+
+    my $namespace = $self->namespace;
+
+    return unless exists $namespace->{$name};
+
+    my $entry_ref = \$namespace->{$name};
+    if (reftype($entry_ref) eq 'GLOB') {
+        if ($type eq 'SCALAR') {
+            if (BROKEN_SCALAR_INITIALIZATION) {
+                return defined ${ *{$entry_ref}{$type} };
+            }
+            else {
+                my $sv = B::svref_2object($entry_ref)->SV;
+                return $sv->isa('B::SV')
+                    || ($sv->isa('B::SPECIAL')
+                     && $B::specialsv_name[$$sv] ne 'Nullsv');
+            }
+        }
+        else {
+            return defined *{$entry_ref}{$type};
+        }
+    }
+    else {
+        # a symbol table entry can be -1 (stub), string (stub with prototype),
+        # or reference (constant)
+        return $type eq 'CODE';
+    }
+}
+
+sub get_symbol {
+    my ($self, $variable, %opts) = @_;
+
+    my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
+
+    my $namespace = $self->namespace;
+
+    if (!exists $namespace->{$name}) {
+        if ($opts{vivify}) {
+            $self->add_symbol($variable);
+        }
+        else {
+            return undef;
+        }
+    }
+
+    my $entry_ref = \$namespace->{$name};
+
+    if (ref($entry_ref) eq 'GLOB') {
+        return *{$entry_ref}{$type};
+    }
+    else {
+        if ($type eq 'CODE') {
+            if (BROKEN_GLOB_ASSIGNMENT || defined($self->{package})) {
+                no strict 'refs';
+                return \&{ $self->name . '::' . $name };
+            }
+
+            # XXX we should really be able to support arbitrary anonymous
+            # stashes here... (not just via Package::Anon)
+            if (blessed($namespace) && $namespace->isa('Package::Anon')) {
+                # ->can will call gv_init for us, which inflates the glob
+                # don't know how to do this in general
+                $namespace->bless(\(my $foo))->can($name);
+            }
+            else {
+                confess "Don't know how to inflate a " . ref($entry_ref)
+                      . " into a full coderef (perhaps you could use"
+                      . " Package::Anon instead of a bare stash?)"
+            }
+
+            return *{ $namespace->{$name} }{CODE};
+        }
+        else {
+            return undef;
+        }
+    }
+}
+
+sub get_or_add_symbol {
+    my $self = shift;
+    $self->get_symbol(@_, vivify => 1);
+}
+
+sub remove_symbol {
+    my ($self, $variable) = @_;
+
+    my ($name, $sigil, $type) = _deconstruct_variable_name($variable);
+
+    # FIXME:
+    # no doubt this is grossly inefficient and
+    # could be done much easier and faster in XS
+
+    my %desc = (
+        SCALAR => { sigil => '$', type => 'SCALAR', name => $name },
+        ARRAY  => { sigil => '@', type => 'ARRAY',  name => $name },
+        HASH   => { sigil => '%', type => 'HASH',   name => $name },
+        CODE   => { sigil => '&', type => 'CODE',   name => $name },
+        IO     => { sigil => '',  type => 'IO',     name => $name },
+    );
+    confess "This should never ever ever happen" if !$desc{$type};
+
+    my @types_to_store = grep { $type ne $_ && $self->has_symbol($desc{$_}) }
+                              keys %desc;
+    my %values = map { $_, $self->get_symbol($desc{$_}) } @types_to_store;
+
+    $values{SCALAR} = $self->get_symbol($desc{SCALAR})
+      if !defined $values{SCALAR}
+        && $type ne 'SCALAR'
+        && BROKEN_SCALAR_INITIALIZATION;
+
+    $self->remove_glob($name);
+
+    $self->add_symbol($desc{$_} => $values{$_})
+        for grep { defined $values{$_} } keys %values;
+}
+
+sub list_all_symbols {
+    my ($self, $type_filter) = @_;
+
+    my $namespace = $self->namespace;
+    if (HAS_ISA_CACHE) {
+        return grep { $_ ne '::ISA::CACHE::' } keys %{$namespace}
+            unless defined $type_filter;
+    }
+    else {
+        return keys %{$namespace}
+            unless defined $type_filter;
+    }
+
+    # NOTE:
+    # or we can filter based on
+    # type (SCALAR|ARRAY|HASH|CODE)
+    if ($type_filter eq 'CODE') {
+        return grep {
+            # any non-typeglob in the symbol table is a constant or stub
+            ref(\$namespace->{$_}) ne 'GLOB'
+                # regular subs are stored in the CODE slot of the typeglob
+                || defined(*{$namespace->{$_}}{CODE})
+        } keys %{$namespace};
+    }
+    elsif ($type_filter eq 'SCALAR') {
+        return grep {
+            !(HAS_ISA_CACHE && $_ eq '::ISA::CACHE::') &&
+            (BROKEN_SCALAR_INITIALIZATION
+                ? (ref(\$namespace->{$_}) eq 'GLOB'
+                      && defined(${*{$namespace->{$_}}{'SCALAR'}}))
+                : (do {
+                      my $entry = \$namespace->{$_};
+                      ref($entry) eq 'GLOB'
+                          && B::svref_2object($entry)->SV->isa('B::SV')
+                  }))
+        } keys %{$namespace};
+    }
+    else {
+        return grep {
+            ref(\$namespace->{$_}) eq 'GLOB'
+                && defined(*{$namespace->{$_}}{$type_filter})
+        } keys %{$namespace};
+    }
+}
+
+sub get_all_symbols {
+    my ($self, $type_filter) = @_;
+
+    my $namespace = $self->namespace;
+    return { %{$namespace} } unless defined $type_filter;
+
+    return {
+        map { $_ => $self->get_symbol({name => $_, type => $type_filter}) }
+            $self->list_all_symbols($type_filter)
+    }
+}
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Package::Stash::PP - Pure perl implementation of the Package::Stash API
+
+=head1 VERSION
+
+version 0.40
+
+=head1 SYNOPSIS
+
+  use Package::Stash;
+
+=head1 DESCRIPTION
+
+This is a backend for L<Package::Stash> implemented in pure perl, for those without a compiler or who would like to use this inline in scripts.
+
+=head1 BUGS
+
+=for stopwords TODO
+
+=over 4
+
+=item * remove_symbol also replaces the associated typeglob
+
+This can cause unexpected behavior when doing manipulation at compile time -
+removing subroutines will still allow them to be called from within the package
+as subroutines (although they will not be available as methods). This can be
+considered a feature in some cases (this is how L<namespace::clean> works, for
+instance), but should not be relied upon - use C<remove_glob> directly if you
+want this behavior.
+
+=item * Some minor memory leaks
+
+The pure perl implementation has a couple minor memory leaks (see the TODO
+tests in t/20-leaks.t) that I'm having a hard time tracking down - these may be
+core perl bugs, it's hard to tell.
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item * L<Class::MOP::Package>
+
+This module is a factoring out of code that used to live here
+
+=back
+
+=for Pod::Coverage BROKEN_ISA_ASSIGNMENT
+add_symbol
+get_all_symbols
+get_or_add_symbol
+get_symbol
+has_symbol
+list_all_symbols
+name
+namespace
+new
+remove_glob
+
+=head1 SUPPORT
+
+Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Package-Stash>
+(or L<bug-Package-Stash@rt.cpan.org|mailto:bug-Package-Stash@rt.cpan.org>).
+
+=head1 AUTHOR
+
+Mostly copied from code from L<Class::MOP::Package>, by Stevan Little and the
+Moose Cabal.
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2022 by Jesse Luehrs.
+
+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
diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd
new file mode 100644 (file)
index 0000000..04f69aa
--- /dev/null
@@ -0,0 +1,161 @@
+do { my $x = {
+       'configure' => {
+                        'requires' => {
+                                        'Dist::CheckConflicts' => '0.02',
+                                        'ExtUtils::MakeMaker' => '0',
+                                        'Text::ParseWords' => '0',
+                                        'perl' => '5.008001'
+                                      }
+                      },
+       'develop' => {
+                      'recommends' => {
+                                        'Dist::Zilla::PluginBundle::Author::ETHER' => '0.162',
+                                        'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007'
+                                      },
+                      'requires' => {
+                                      'Encode' => '0',
+                                      'ExtUtils::HasCompiler' => '0.014',
+                                      'File::Spec' => '0',
+                                      'IO::Handle' => '0',
+                                      'IPC::Open3' => '0',
+                                      'Package::Anon' => '0',
+                                      'Pod::Wordlist' => '0',
+                                      'Test::CPAN::Changes' => '0.19',
+                                      'Test::CPAN::Meta' => '0',
+                                      'Test::EOL' => '0',
+                                      'Test::Fatal' => '0',
+                                      'Test::Kwalitee' => '1.21',
+                                      'Test::LeakTrace' => '0',
+                                      'Test::MinimumVersion' => '0',
+                                      'Test::Mojibake' => '0',
+                                      'Test::More' => '0.96',
+                                      'Test::NoTabs' => '0',
+                                      'Test::Pod' => '1.41',
+                                      'Test::Pod::No404s' => '0',
+                                      'Test::Portability::Files' => '0',
+                                      'Test::Spelling' => '0.12',
+                                      'Variable::Magic' => '0',
+                                      'lib' => '0'
+                                    }
+                    },
+       'runtime' => {
+                      'recommends' => {
+                                        'Package::Stash::XS' => '0.26'
+                                      },
+                      'requires' => {
+                                      'B' => '0',
+                                      'Carp' => '0',
+                                      'Dist::CheckConflicts' => '0.02',
+                                      'Getopt::Long' => '0',
+                                      'Module::Implementation' => '0.06',
+                                      'Scalar::Util' => '0',
+                                      'Symbol' => '0',
+                                      'constant' => '0',
+                                      'perl' => '5.008001',
+                                      'strict' => '0',
+                                      'warnings' => '0'
+                                    }
+                    },
+       'test' => {
+                   'recommends' => {
+                                     'CPAN::Meta' => '2.120900'
+                                   },
+                   'requires' => {
+                                   'CPAN::Meta::Check' => '0.011',
+                                   'CPAN::Meta::Requirements' => '0',
+                                   'ExtUtils::MakeMaker' => '0',
+                                   'File::Spec' => '0',
+                                   'Test::Fatal' => '0',
+                                   'Test::More' => '0.88',
+                                   'Test::Needs' => '0',
+                                   'base' => '0',
+                                   'lib' => '0',
+                                   'perl' => '5.008001'
+                                 },
+                   'suggests' => {
+                                   'Variable::Magic' => '0'
+                                 }
+                 },
+       'x_Dist_Zilla' => {
+                           'requires' => {
+                                           'Dist::Zilla' => '5',
+                                           'Dist::Zilla::Plugin::Authority' => '1.009',
+                                           'Dist::Zilla::Plugin::AutoMetaResources' => '0',
+                                           'Dist::Zilla::Plugin::AutoPrereqs' => '5.038',
+                                           'Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional' => '0.004',
+                                           'Dist::Zilla::Plugin::CheckIssues' => '0',
+                                           'Dist::Zilla::Plugin::CheckMetaResources' => '0',
+                                           'Dist::Zilla::Plugin::CheckPrereqsIndexed' => '0.019',
+                                           'Dist::Zilla::Plugin::CheckSelfDependency' => '0',
+                                           'Dist::Zilla::Plugin::CheckStrictVersion' => '0',
+                                           'Dist::Zilla::Plugin::ConfirmRelease' => '0',
+                                           'Dist::Zilla::Plugin::Conflicts' => '0',
+                                           'Dist::Zilla::Plugin::CopyFilesFromRelease' => '0',
+                                           'Dist::Zilla::Plugin::DynamicPrereqs' => '0.029',
+                                           'Dist::Zilla::Plugin::EnsureLatestPerl' => '0',
+                                           'Dist::Zilla::Plugin::ExecDir' => '0',
+                                           'Dist::Zilla::Plugin::FileFinder::ByName' => '0',
+                                           'Dist::Zilla::Plugin::GenerateFile::FromShareDir' => '0',
+                                           'Dist::Zilla::Plugin::Git::Check' => '0',
+                                           'Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch' => '0.004',
+                                           'Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts' => '0',
+                                           'Dist::Zilla::Plugin::Git::Commit' => '2.020',
+                                           'Dist::Zilla::Plugin::Git::Contributors' => '0.029',
+                                           'Dist::Zilla::Plugin::Git::Describe' => '0.004',
+                                           'Dist::Zilla::Plugin::Git::GatherDir' => '2.016',
+                                           'Dist::Zilla::Plugin::Git::Push' => '0',
+                                           'Dist::Zilla::Plugin::Git::Remote::Check' => '0',
+                                           'Dist::Zilla::Plugin::Git::Tag' => '0',
+                                           'Dist::Zilla::Plugin::GitHub::Update' => '0.40',
+                                           'Dist::Zilla::Plugin::GithubMeta' => '0.54',
+                                           'Dist::Zilla::Plugin::InstallGuide' => '1.200005',
+                                           'Dist::Zilla::Plugin::Keywords' => '0.004',
+                                           'Dist::Zilla::Plugin::License' => '5.038',
+                                           'Dist::Zilla::Plugin::MakeMaker' => '0',
+                                           'Dist::Zilla::Plugin::Manifest' => '0',
+                                           'Dist::Zilla::Plugin::MetaConfig' => '0',
+                                           'Dist::Zilla::Plugin::MetaJSON' => '0',
+                                           'Dist::Zilla::Plugin::MetaNoIndex' => '0',
+                                           'Dist::Zilla::Plugin::MetaProvides::Package' => '1.15000002',
+                                           'Dist::Zilla::Plugin::MetaTests' => '0',
+                                           'Dist::Zilla::Plugin::MetaYAML' => '0',
+                                           'Dist::Zilla::Plugin::MinimumPerl' => '1.006',
+                                           'Dist::Zilla::Plugin::MojibakeTests' => '0.8',
+                                           'Dist::Zilla::Plugin::NextRelease' => '5.033',
+                                           'Dist::Zilla::Plugin::PodSyntaxTests' => '5.040',
+                                           'Dist::Zilla::Plugin::Prereqs' => '0',
+                                           'Dist::Zilla::Plugin::Prereqs::AuthorDeps' => '0.006',
+                                           'Dist::Zilla::Plugin::Prereqs::Soften' => '0',
+                                           'Dist::Zilla::Plugin::PromptIfStale' => '0',
+                                           'Dist::Zilla::Plugin::Readme' => '0',
+                                           'Dist::Zilla::Plugin::ReadmeAnyFromPod' => '0.142180',
+                                           'Dist::Zilla::Plugin::RewriteVersion::Transitional' => '0.006',
+                                           'Dist::Zilla::Plugin::Run::AfterBuild' => '0.041',
+                                           'Dist::Zilla::Plugin::Run::AfterRelease' => '0.038',
+                                           'Dist::Zilla::Plugin::RunExtraTests' => '0.024',
+                                           'Dist::Zilla::Plugin::StaticInstall' => '0.005',
+                                           'Dist::Zilla::Plugin::SurgicalPodWeaver' => '0',
+                                           'Dist::Zilla::Plugin::Test::CPAN::Changes' => '0.012',
+                                           'Dist::Zilla::Plugin::Test::ChangesHasContent' => '0',
+                                           'Dist::Zilla::Plugin::Test::CheckBreaks' => '0',
+                                           'Dist::Zilla::Plugin::Test::Compile' => '2.039',
+                                           'Dist::Zilla::Plugin::Test::EOL' => '0.17',
+                                           'Dist::Zilla::Plugin::Test::Kwalitee' => '2.10',
+                                           'Dist::Zilla::Plugin::Test::MinimumVersion' => '2.000010',
+                                           'Dist::Zilla::Plugin::Test::NoTabs' => '0.08',
+                                           'Dist::Zilla::Plugin::Test::Pod::No404s' => '1.003',
+                                           'Dist::Zilla::Plugin::Test::PodSpelling' => '2.006003',
+                                           'Dist::Zilla::Plugin::Test::Portability' => '2.000007',
+                                           'Dist::Zilla::Plugin::Test::ReportPrereqs' => '0.022',
+                                           'Dist::Zilla::Plugin::TestRelease' => '0',
+                                           'Dist::Zilla::Plugin::UploadToCPAN' => '0',
+                                           'Dist::Zilla::Plugin::UseUnsafeInc' => '0',
+                                           'Dist::Zilla::PluginBundle::Author::ETHER' => '0.119',
+                                           'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007',
+                                           'ExtUtils::HasCompiler' => '0.014',
+                                           'Software::License::Perl_5' => '0'
+                                         }
+                         }
+     };
+  $x;
+ }
\ No newline at end of file
diff --git a/t/00-report-prereqs.t b/t/00-report-prereqs.t
new file mode 100644 (file)
index 0000000..7220a2a
--- /dev/null
@@ -0,0 +1,199 @@
+#!perl
+
+use strict;
+use warnings;
+
+# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.028
+
+use Test::More tests => 1;
+
+use ExtUtils::MakeMaker;
+use File::Spec;
+
+# from $version::LAX
+my $lax_version_re =
+    qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
+            |
+            (?:\.[0-9]+) (?:_[0-9]+)?
+        ) | (?:
+            v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
+            |
+            (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
+        )
+    )/x;
+
+# hide optional CPAN::Meta modules from prereq scanner
+# and check if they are available
+my $cpan_meta = "CPAN::Meta";
+my $cpan_meta_pre = "CPAN::Meta::Prereqs";
+my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic
+
+# Verify requirements?
+my $DO_VERIFY_PREREQS = 1;
+
+sub _max {
+    my $max = shift;
+    $max = ( $_ > $max ) ? $_ : $max for @_;
+    return $max;
+}
+
+sub _merge_prereqs {
+    my ($collector, $prereqs) = @_;
+
+    # CPAN::Meta::Prereqs object
+    if (ref $collector eq $cpan_meta_pre) {
+        return $collector->with_merged_prereqs(
+            CPAN::Meta::Prereqs->new( $prereqs )
+        );
+    }
+
+    # Raw hashrefs
+    for my $phase ( keys %$prereqs ) {
+        for my $type ( keys %{ $prereqs->{$phase} } ) {
+            for my $module ( keys %{ $prereqs->{$phase}{$type} } ) {
+                $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module};
+            }
+        }
+    }
+
+    return $collector;
+}
+
+my @include = qw(
+  Encode
+  File::Temp
+  JSON::PP
+  Module::Runtime
+  Sub::Name
+  YAML
+  autodie
+);
+
+my @exclude = qw(
+
+);
+
+# Add static prereqs to the included modules list
+my $static_prereqs = do './t/00-report-prereqs.dd';
+
+# Merge all prereqs (either with ::Prereqs or a hashref)
+my $full_prereqs = _merge_prereqs(
+    ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ),
+    $static_prereqs
+);
+
+# Add dynamic prereqs to the included modules list (if we can)
+my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
+my $cpan_meta_error;
+if ( $source && $HAS_CPAN_META
+    && (my $meta = eval { CPAN::Meta->load_file($source) } )
+) {
+    $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs);
+}
+else {
+    $cpan_meta_error = $@;    # capture error from CPAN::Meta->load_file($source)
+    $source = 'static metadata';
+}
+
+my @full_reports;
+my @dep_errors;
+my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs;
+
+# Add static includes into a fake section
+for my $mod (@include) {
+    $req_hash->{other}{modules}{$mod} = 0;
+}
+
+for my $phase ( qw(configure build test runtime develop other) ) {
+    next unless $req_hash->{$phase};
+    next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING});
+
+    for my $type ( qw(requires recommends suggests conflicts modules) ) {
+        next unless $req_hash->{$phase}{$type};
+
+        my $title = ucfirst($phase).' '.ucfirst($type);
+        my @reports = [qw/Module Want Have/];
+
+        for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) {
+            next if $mod eq 'perl';
+            next if grep { $_ eq $mod } @exclude;
+
+            my $file = $mod;
+            $file =~ s{::}{/}g;
+            $file .= ".pm";
+            my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC;
+
+            my $want = $req_hash->{$phase}{$type}{$mod};
+            $want = "undef" unless defined $want;
+            $want = "any" if !$want && $want == 0;
+
+            my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
+
+            if ($prefix) {
+                my $have = MM->parse_version( File::Spec->catfile($prefix, $file) );
+                $have = "undef" unless defined $have;
+                push @reports, [$mod, $want, $have];
+
+                if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) {
+                    if ( $have !~ /\A$lax_version_re\z/ ) {
+                        push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)";
+                    }
+                    elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
+                        push @dep_errors, "$mod version '$have' is not in required range '$want'";
+                    }
+                }
+            }
+            else {
+                push @reports, [$mod, $want, "missing"];
+
+                if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) {
+                    push @dep_errors, "$mod is not installed ($req_string)";
+                }
+            }
+        }
+
+        if ( @reports ) {
+            push @full_reports, "=== $title ===\n\n";
+
+            my $ml = _max( map { length $_->[0] } @reports );
+            my $wl = _max( map { length $_->[1] } @reports );
+            my $hl = _max( map { length $_->[2] } @reports );
+
+            if ($type eq 'modules') {
+                splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl];
+                push @full_reports, map { sprintf("    %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports;
+            }
+            else {
+                splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl];
+                push @full_reports, map { sprintf("    %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports;
+            }
+
+            push @full_reports, "\n";
+        }
+    }
+}
+
+if ( @full_reports ) {
+    diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports;
+}
+
+if ( $cpan_meta_error || @dep_errors ) {
+    diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n";
+}
+
+if ( $cpan_meta_error ) {
+    my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
+    diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n";
+}
+
+if ( @dep_errors ) {
+    diag join("\n",
+        "\nThe following REQUIRED prerequisites were not satisfied:\n",
+        @dep_errors,
+        "\n"
+    );
+}
+
+pass('Reported prereqs');
+
+# vim: ts=4 sts=4 sw=4 et:
diff --git a/t/addsub.t b/t/addsub.t
new file mode 100644 (file)
index 0000000..627cc04
--- /dev/null
@@ -0,0 +1,45 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+BEGIN { $^P |= 0x210 } # PERLDBf_SUBLINE
+
+use Package::Stash;
+
+my $foo_stash = Package::Stash->new('Foo');
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo::{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk", __LINE__ });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo::{funk}), '... the &funk slot was created successfully');
+
+{
+    no strict 'refs';
+    ok(defined &{'Foo::funk'}, '... our &funk exists');
+}
+
+is((Foo->funk())[0], 'Foo::funk', '... got the right value from the function');
+
+my $line = (Foo->funk())[1];
+is $DB::sub{'Foo::funk'}, sprintf( "%s:%d-%d", __FILE__, $line, $line ),
+    '... got the right %DB::sub value for funk default args';
+
+$foo_stash->add_symbol(
+    '&dunk'        => sub { "Foo::dunk" },
+    filename       => "FileName",
+    first_line_num => 100,
+    last_line_num  => 199
+);
+
+is $DB::sub{'Foo::dunk'}, sprintf( "%s:%d-%d", "FileName", 100, 199 ),
+    '... got the right %DB::sub value for dunk with specified args';
+
+done_testing;
diff --git a/t/anon-basic.t b/t/anon-basic.t
new file mode 100644 (file)
index 0000000..649a7d8
--- /dev/null
@@ -0,0 +1,405 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use Package::Stash;
+
+BEGIN {
+    plan skip_all => "Anonymous stashes in PP need at least perl 5.14"
+        if $] < 5.014
+        && $Package::Stash::IMPLEMENTATION eq 'PP';
+}
+
+use Test::Needs 'Package::Anon';
+use Symbol;
+
+my $Foo = Package::Anon->new('Foo');
+$Foo->{SOME_CONSTANT} = \1;
+
+# ----------------------------------------------------------------------
+## tests adding a HASH
+
+my $foo_stash = Package::Stash->new($Foo);
+ok(!defined($Foo->{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the object agrees');
+ok(!defined($Foo->{foo}), '... checking doesn\'t vivify');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... created %Foo::foo successfully');
+
+# ... scalar should NOT be created here
+
+ok(!$foo_stash->has_symbol('$foo'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@foo'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&foo'), '... CODE shouldnt have been created too');
+
+ok(defined($Foo->{foo}), '... the %foo slot was created successfully');
+ok($foo_stash->has_symbol('%foo'), '... the meta agrees');
+
+# check the value ...
+
+ok(exists $Foo->{foo}{one}, '... our %foo was initialized correctly');
+is($Foo->{foo}{one}, 1, '... our %foo was initialized correctly');
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+# ... make sure changes propogate up
+
+$foo->{two} = 2;
+
+is(\%{ $Foo->{foo} }, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the metas');
+
+ok(exists ${ $Foo->{foo} }{two}, '... our %foo was updated correctly');
+is(${ $Foo->{foo} }{two}, 2, '... our %foo was updated correctly');
+
+# ----------------------------------------------------------------------
+## test adding an ARRAY
+
+ok(!defined($Foo->{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(defined($Foo->{bar}), '... the @bar slot was created successfully');
+ok($foo_stash->has_symbol('@bar'), '... the meta agrees');
+
+# ... why does this not work ...
+
+ok(!$foo_stash->has_symbol('$bar'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%bar'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&bar'), '... CODE shouldnt have been created too');
+
+# check the value itself
+
+is(scalar @{ $Foo->{bar} }, 3, '... our @bar was initialized correctly');
+is($Foo->{bar}[1], 2, '... our @bar was initialized correctly');
+
+# ----------------------------------------------------------------------
+## test adding a SCALAR
+
+ok(!defined($Foo->{baz}), '... the $baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('$baz' => 10);
+}, undef, '... created $Foo::baz successfully');
+
+ok(defined($Foo->{baz}), '... the $baz slot was created successfully');
+ok($foo_stash->has_symbol('$baz'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('@baz'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%baz'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&baz'), '... CODE shouldnt have been created too');
+
+is(${$foo_stash->get_symbol('$baz')}, 10, '... got the right value back');
+
+${ $Foo->{baz} } = 1;
+
+is(${ $Foo->{baz} }, 1, '... our $baz was assigned to correctly');
+is(${$foo_stash->get_symbol('$baz')}, 1, '... the meta agrees');
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo->{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk" });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo->{funk}), '... the &funk slot was created successfully');
+ok($foo_stash->has_symbol('&funk'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('$funk'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@funk'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%funk'), '... HASH shouldnt have been created too');
+
+ok(defined &{ $Foo->{funk} }, '... our &funk exists');
+
+is($Foo->bless({})->funk(), 'Foo::funk', '... got the right value from the function');
+
+# ----------------------------------------------------------------------
+## test multiple slots in the glob
+
+my $ARRAY = [ 1, 2, 3 ];
+my $CODE = sub { "Foo::foo" };
+
+is(exception {
+    $foo_stash->add_symbol('@foo' => $ARRAY);
+}, undef, '... created @Foo::foo successfully');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot was added successfully');
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('&foo' => $CODE);
+}, undef, '... created &Foo::foo successfully');
+
+ok($foo_stash->has_symbol('&foo'), '... the meta agrees');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('$foo' => 'Foo::foo');
+}, undef, '... created $Foo::foo successfully');
+
+ok($foo_stash->has_symbol('$foo'), '... the meta agrees');
+my $SCALAR = $foo_stash->get_symbol('$foo');
+is($$SCALAR, 'Foo::foo', '... got the right scalar value back');
+
+is(${ $Foo->{foo} }, 'Foo::foo', '... got the right value from the scalar');
+
+is(exception {
+    $foo_stash->remove_symbol('%foo');
+}, undef, '... removed %Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('%foo'), '... the %foo slot was removed successfully');
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('&foo'), '... the &foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+ok(defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has NOT been removed');
+ok(defined(${ $Foo->{foo} }), '... the $foo slot has NOT been removed');
+
+is(exception {
+    $foo_stash->remove_symbol('&foo');
+}, undef, '... removed &Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('&foo'), '... the &foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(!defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has now been removed');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+ok(defined(${ $Foo->{foo} }), '... the $foo slot has NOT been removed');
+
+is(exception {
+    $foo_stash->remove_symbol('$foo');
+}, undef, '... removed $Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('$foo'), '... the $foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(!defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has now been removed');
+ok(!defined(${ $Foo->{foo} }), '... the $foo slot has now been removed');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+
+{
+    my $syms = $foo_stash->get_all_symbols;
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols('CODE');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('CODE') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('&' . $symbol), '... got the right symbol');
+    }
+}
+
+{
+    $foo_stash->add_symbol('%bare');
+    ok(!$foo_stash->has_symbol('$bare'),
+       "add_symbol with single argument doesn't vivify scalar slot");
+}
+
+{
+    $foo_stash->add_symbol('%zork', {});
+
+    my $syms = $foo_stash->get_all_symbols('HASH');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('HASH') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('%' . $symbol), '... got the right symbol');
+    }
+
+    is_deeply(
+        $syms,
+        {
+            zork => *{ $Foo->{zork} }{HASH},
+            bare => *{ $Foo->{bare} }{HASH},
+        },
+        "got the right ones",
+    );
+}
+
+# check some errors
+
+like(exception {
+    $foo_stash->add_symbol('@bar', {})
+}, qr/HASH.*is not of type ARRAY/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('bar', [])
+}, qr/ARRAY.*is not of type IO/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', sub { })
+}, qr/CODE.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', *{ Symbol::geniosym() }{IO})
+}, qr/IO.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+is_deeply([Package::Stash->new('Foo')->list_all_symbols], [],
+          "Foo:: isn't touched");
+
+# *{ $Quux->{foo} } = \23 doesn't work on 5.12 and lower, apparently
+my $Quux = Package::Anon->new('Quux');
+{
+    my $gv = Symbol::gensym;
+    *$gv = \23;
+    *$gv = ["bar"];
+    *$gv = { baz => 1 };
+    *$gv = sub { };
+    *$gv = *{ Symbol::geniosym() }{IO};
+    $Quux->{foo} = *$gv;
+}
+
+{
+    my $stash = Package::Stash->new($Quux);
+
+    my %expect = (
+        '$foo' => \23,
+        '@foo' => ["bar"],
+        '%foo' => { baz => 1 },
+        '&foo' => \&{ $Quux->{foo} },
+        'foo'  => *{ $Quux->{foo} }{IO},
+    );
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 42});
+
+    $expect{'%bar'} = {x => 42};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 43});
+
+    $expect{'%bar'} = {x => 43};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+}
+
+is_deeply([Package::Stash->new('Quux')->list_all_symbols], [],
+          "Quux:: isn't touched");
+
+my $Quuux = Package::Anon->new('Quuux');
+
+{
+    my $gv = Symbol::gensym;
+    *$gv = \(my $scalar);
+    *$gv = [];
+    $Quuux->{foo} = *$gv;
+}
+
+{
+    my $gv = Symbol::gensym;
+    *$gv = [];
+    $Quuux->{bar} = *$gv;
+}
+
+{
+    my $gv = Symbol::gensym;
+    *$gv = {};
+    *$gv = sub { };
+    $Quuux->{baz} = *$gv;
+}
+
+$Quuux->{quux} = \1;
+
+$Quuux->{quuux} = \[];
+
+$Quuux->{quuuux} = -1;
+
+{
+    my $quuux = Package::Stash->new($Quuux);
+    is_deeply(
+        # Package::Anon adds a couple methods
+        [grep { $_ ne 'isa' && $_ ne 'can' } sort $quuux->list_all_symbols],
+        [qw(bar baz foo quuuux quuux quux)],
+        "list_all_symbols",
+    );
+    { local $TODO = $] < 5.010
+          ? "undef scalars aren't visible on 5.8"
+          : undef;
+    is_deeply(
+        [sort $quuux->list_all_symbols('SCALAR')],
+        [qw(foo)],
+        "list_all_symbols SCALAR",
+    );
+    }
+    is_deeply(
+        [sort $quuux->list_all_symbols('ARRAY')],
+        [qw(bar foo)],
+        "list_all_symbols ARRAY",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('HASH')],
+        [qw(baz)],
+        "list_all_symbols HASH",
+    );
+    is_deeply(
+        # Package::Anon adds a couple methods
+        [grep { $_ ne 'isa' && $_ ne 'can' } sort $quuux->list_all_symbols('CODE')],
+        [qw(baz quuuux quuux quux)],
+        "list_all_symbols CODE",
+    );
+}
+
+is_deeply([Package::Stash->new('Quuux')->list_all_symbols], [],
+          "Quuux:: isn't touched");
+
+done_testing;
diff --git a/t/anon.t b/t/anon.t
new file mode 100644 (file)
index 0000000..7ec63f8
--- /dev/null
+++ b/t/anon.t
@@ -0,0 +1,50 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+use lib 't/lib';
+
+use Package::Stash;
+
+BEGIN {
+    plan skip_all => "Anonymous stashes in PP need at least perl 5.14"
+        if $] < 5.014
+        && $Package::Stash::IMPLEMENTATION eq 'PP';
+}
+
+use Test::Needs 'Package::Anon';
+use Symbol;
+
+my $anon = Package::Anon->new;
+my $stash = Package::Stash->new($anon);
+my $obj = $anon->bless({});
+
+{
+    my $code = sub { 'FOO' };
+    $stash->add_symbol('&foo' => $code);
+    is($stash->get_symbol('&foo'), $code);
+    is($obj->foo, 'FOO');
+}
+
+{
+    $anon->{bar} = \123;
+
+    my $code = $stash->get_symbol('&bar');
+    is(ref($code), 'CODE');
+    is($code->(), 123);
+
+    is($obj->bar, 123);
+}
+
+{
+    $anon->{baz} = -1;
+
+    my $code = $stash->get_symbol('&baz');
+    is(ref($code), 'CODE');
+    like(
+        exception { $code->() },
+        qr/Undefined subroutine \&__ANON__::baz called/
+    );
+}
+
+done_testing;
diff --git a/t/bare-anon-basic.t b/t/bare-anon-basic.t
new file mode 100644 (file)
index 0000000..1cf09e9
--- /dev/null
@@ -0,0 +1,399 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use Package::Stash;
+
+BEGIN {
+    plan skip_all => "Anonymous stashes in PP need at least perl 5.14"
+        if $] < 5.014
+        && $Package::Stash::IMPLEMENTATION eq 'PP';
+
+    plan skip_all => "This isn't really going to work yet, probably";
+}
+
+use Symbol;
+
+my $Foo = {};
+$Foo->{SOME_CONSTANT} = \1;
+
+# ----------------------------------------------------------------------
+## tests adding a HASH
+
+my $foo_stash = Package::Stash->new($Foo);
+ok(!defined($Foo->{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the object agrees');
+ok(!defined($Foo->{foo}), '... checking doesn\'t vivify');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... created %Foo::foo successfully');
+
+# ... scalar should NOT be created here
+
+ok(!$foo_stash->has_symbol('$foo'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@foo'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&foo'), '... CODE shouldnt have been created too');
+
+ok(defined($Foo->{foo}), '... the %foo slot was created successfully');
+ok($foo_stash->has_symbol('%foo'), '... the meta agrees');
+
+# check the value ...
+
+ok(exists $Foo->{foo}{one}, '... our %foo was initialized correctly');
+is($Foo->{foo}{one}, 1, '... our %foo was initialized correctly');
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+# ... make sure changes propogate up
+
+$foo->{two} = 2;
+
+is(\%{ $Foo->{foo} }, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the metas');
+
+ok(exists ${ $Foo->{foo} }{two}, '... our %foo was updated correctly');
+is(${ $Foo->{foo} }{two}, 2, '... our %foo was updated correctly');
+
+# ----------------------------------------------------------------------
+## test adding an ARRAY
+
+ok(!defined($Foo->{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(defined($Foo->{bar}), '... the @bar slot was created successfully');
+ok($foo_stash->has_symbol('@bar'), '... the meta agrees');
+
+# ... why does this not work ...
+
+ok(!$foo_stash->has_symbol('$bar'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%bar'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&bar'), '... CODE shouldnt have been created too');
+
+# check the value itself
+
+is(scalar @{ $Foo->{bar} }, 3, '... our @bar was initialized correctly');
+is($Foo->{bar}[1], 2, '... our @bar was initialized correctly');
+
+# ----------------------------------------------------------------------
+## test adding a SCALAR
+
+ok(!defined($Foo->{baz}), '... the $baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('$baz' => 10);
+}, undef, '... created $Foo::baz successfully');
+
+ok(defined($Foo->{baz}), '... the $baz slot was created successfully');
+ok($foo_stash->has_symbol('$baz'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('@baz'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%baz'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&baz'), '... CODE shouldnt have been created too');
+
+is(${$foo_stash->get_symbol('$baz')}, 10, '... got the right value back');
+
+${ $Foo->{baz} } = 1;
+
+is(${ $Foo->{baz} }, 1, '... our $baz was assigned to correctly');
+is(${$foo_stash->get_symbol('$baz')}, 1, '... the meta agrees');
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo->{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk" });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo->{funk}), '... the &funk slot was created successfully');
+ok($foo_stash->has_symbol('&funk'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('$funk'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@funk'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%funk'), '... HASH shouldnt have been created too');
+
+ok(defined &{ $Foo->{funk} }, '... our &funk exists');
+
+# can't bless things into hashrefs yet
+# is($Foo->bless({})->funk(), 'Foo::funk', '... got the right value from the function');
+
+# ----------------------------------------------------------------------
+## test multiple slots in the glob
+
+my $ARRAY = [ 1, 2, 3 ];
+my $CODE = sub { "Foo::foo" };
+
+is(exception {
+    $foo_stash->add_symbol('@foo' => $ARRAY);
+}, undef, '... created @Foo::foo successfully');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot was added successfully');
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('&foo' => $CODE);
+}, undef, '... created &Foo::foo successfully');
+
+ok($foo_stash->has_symbol('&foo'), '... the meta agrees');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('$foo' => 'Foo::foo');
+}, undef, '... created $Foo::foo successfully');
+
+ok($foo_stash->has_symbol('$foo'), '... the meta agrees');
+my $SCALAR = $foo_stash->get_symbol('$foo');
+is($$SCALAR, 'Foo::foo', '... got the right scalar value back');
+
+is(${ $Foo->{foo} }, 'Foo::foo', '... got the right value from the scalar');
+
+is(exception {
+    $foo_stash->remove_symbol('%foo');
+}, undef, '... removed %Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('%foo'), '... the %foo slot was removed successfully');
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('&foo'), '... the &foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+ok(defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has NOT been removed');
+ok(defined(${ $Foo->{foo} }), '... the $foo slot has NOT been removed');
+
+is(exception {
+    $foo_stash->remove_symbol('&foo');
+}, undef, '... removed &Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('&foo'), '... the &foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(!defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has now been removed');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+ok(defined(${ $Foo->{foo} }), '... the $foo slot has NOT been removed');
+
+is(exception {
+    $foo_stash->remove_symbol('$foo');
+}, undef, '... removed $Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('$foo'), '... the $foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+ok(!defined(*{ $Foo->{foo} }{HASH}), '... the %foo slot has been removed successfully');
+ok(!defined(*{ $Foo->{foo} }{CODE}), '... the &foo slot has now been removed');
+ok(!defined(${ $Foo->{foo} }), '... the $foo slot has now been removed');
+ok(defined(*{ $Foo->{foo} }{ARRAY}), '... the @foo slot has NOT been removed');
+
+{
+    my $syms = $foo_stash->get_all_symbols;
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+}
+
+{
+    local $TODO = "can't inflate weird stash entries";
+
+    is(
+        exception {
+            my $syms = $foo_stash->get_all_symbols('CODE');
+
+            is_deeply(
+                [ sort keys %{ $syms } ],
+                [ sort $foo_stash->list_all_symbols('CODE') ],
+                '... the fetched symbols are the same as the listed ones'
+            );
+
+            foreach my $symbol (keys %{ $syms }) {
+                is($syms->{$symbol}, $foo_stash->get_symbol('&' . $symbol), '... got the right symbol');
+            }
+        },
+        undef
+    );
+}
+
+{
+    $foo_stash->add_symbol('%bare');
+    ok(!$foo_stash->has_symbol('$bare'),
+       "add_symbol with single argument doesn't vivify scalar slot");
+}
+
+{
+    $foo_stash->add_symbol('%zork', {});
+
+    my $syms = $foo_stash->get_all_symbols('HASH');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('HASH') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('%' . $symbol), '... got the right symbol');
+    }
+
+    is_deeply(
+        $syms,
+        {
+            zork => *{ $Foo->{zork} }{HASH},
+            bare => *{ $Foo->{bare} }{HASH},
+        },
+        "got the right ones",
+    );
+}
+
+# check some errors
+
+like(exception {
+    $foo_stash->add_symbol('@bar', {})
+}, qr/HASH.*is not of type ARRAY/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('bar', [])
+}, qr/ARRAY.*is not of type IO/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', sub { })
+}, qr/CODE.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', *{ Symbol::geniosym() }{IO})
+}, qr/IO.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+is_deeply([Package::Stash->new('Foo')->list_all_symbols], [],
+          "Foo:: isn't touched");
+
+my $Quux = {};
+$Quux->{foo} = *{ Symbol::gensym() };
+*{ $Quux->{foo} } = \23;
+*{ $Quux->{foo} } = ["bar"];
+*{ $Quux->{foo} } = { baz => 1 };
+*{ $Quux->{foo} } = sub { };
+*{ $Quux->{foo} } = *{ Symbol::geniosym() }{IO};
+
+{
+    my $stash = Package::Stash->new($Quux);
+
+    my %expect = (
+        '$foo' => \23,
+        '@foo' => ["bar"],
+        '%foo' => { baz => 1 },
+        '&foo' => \&{ $Quux->{foo} },
+        'foo'  => *{ $Quux->{foo} }{IO},
+    );
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 42});
+
+    $expect{'%bar'} = {x => 42};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 43});
+
+    $expect{'%bar'} = {x => 43};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+}
+
+is_deeply([Package::Stash->new('Quux')->list_all_symbols], [],
+          "Quux:: isn't touched");
+
+my $Quuux = {};
+
+$Quuux->{foo} = *{ Symbol::gensym() };
+*{ $Quuux->{foo} } = \(my $scalar);
+*{ $Quuux->{foo} } = [];
+
+$Quuux->{bar} = *{ Symbol::gensym() };
+*{ $Quuux->{bar} } = [];
+
+$Quuux->{baz} = *{ Symbol::gensym() };
+*{ $Quuux->{baz} } = {};
+*{ $Quuux->{baz} } = sub { };
+
+$Quuux->{quux} = \1;
+
+$Quuux->{quuux} = \[];
+
+$Quuux->{quuuux} = -1;
+
+{
+    my $quuux = Package::Stash->new($Quuux);
+    is_deeply(
+        [sort $quuux->list_all_symbols],
+        [qw(bar baz foo quuuux quuux quux)],
+        "list_all_symbols",
+    );
+    { local $TODO = $] < 5.010
+          ? "undef scalars aren't visible on 5.8"
+          : undef;
+    is_deeply(
+        [sort $quuux->list_all_symbols('SCALAR')],
+        [qw(foo)],
+        "list_all_symbols SCALAR",
+    );
+    }
+    is_deeply(
+        [sort $quuux->list_all_symbols('ARRAY')],
+        [qw(bar foo)],
+        "list_all_symbols ARRAY",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('HASH')],
+        [qw(baz)],
+        "list_all_symbols HASH",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('CODE')],
+        [qw(baz quuuux quuux quux)],
+        "list_all_symbols CODE",
+    );
+}
+
+is_deeply([Package::Stash->new('Quuux')->list_all_symbols], [],
+          "Quuux:: isn't touched");
+
+done_testing;
diff --git a/t/bare-anon.t b/t/bare-anon.t
new file mode 100644 (file)
index 0000000..605183e
--- /dev/null
@@ -0,0 +1,64 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+use lib 't/lib';
+
+use Package::Stash;
+
+BEGIN {
+    plan skip_all => "Anonymous stashes in PP need at least perl 5.14"
+        if $] < 5.014
+        && $Package::Stash::IMPLEMENTATION eq 'PP';
+
+    plan skip_all => "This isn't really going to work yet, probably";
+}
+
+use Symbol;
+
+my $anon = {};
+my $stash = Package::Stash->new($anon);
+# no way to bless something into a hashref yet
+# my $obj = $anon->bless({});
+
+{
+    my $code = sub { 'FOO' };
+    $stash->add_symbol('&foo' => $code);
+    is($stash->get_symbol('&foo'), $code);
+    # is($obj->foo, 'FOO');
+}
+
+{
+    local $TODO = "can't inflate weird stash entries";
+    $anon->{bar} = \123;
+
+    is(
+        exception {
+            my $code = $stash->get_symbol('&bar');
+            is(ref($code), 'CODE');
+            is($code->(), 123);
+
+            # is($obj->bar, 123);
+        },
+        undef
+    );
+}
+
+{
+    local $TODO = "can't inflate weird stash entries";
+    $anon->{baz} = -1;
+
+    is(
+        exception {
+            my $code = $stash->get_symbol('&baz');
+            is(ref($code), 'CODE');
+            like(
+                exception { $code->() },
+                qr/Undefined subroutine \&__ANON__::baz called/
+            );
+        },
+        undef
+    );
+}
+
+done_testing;
diff --git a/t/basic.t b/t/basic.t
new file mode 100644 (file)
index 0000000..770df78
--- /dev/null
+++ b/t/basic.t
@@ -0,0 +1,447 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use Package::Stash;
+
+like(exception { Package::Stash->name }, qr/Can't call name as a class method/,
+   q{... can't call name() as a class method});
+
+{
+    package Foo;
+
+    use constant SOME_CONSTANT => 1;
+}
+
+# ----------------------------------------------------------------------
+## tests adding a HASH
+
+my $foo_stash = Package::Stash->new('Foo');
+ok(!defined($Foo::{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the object agrees');
+ok(!defined($Foo::{foo}), '... checking doesn\' vivify');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... created %Foo::foo successfully');
+
+# ... scalar should NOT be created here
+
+ok(!$foo_stash->has_symbol('$foo'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@foo'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&foo'), '... CODE shouldnt have been created too');
+
+ok(defined($Foo::{foo}), '... the %foo slot was created successfully');
+ok($foo_stash->has_symbol('%foo'), '... the meta agrees');
+
+# check the value ...
+
+{
+    no strict 'refs';
+    ok(exists ${'Foo::foo'}{one}, '... our %foo was initialized correctly');
+    is(${'Foo::foo'}{one}, 1, '... our %foo was initialized correctly');
+}
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+# ... make sure changes propogate up
+
+$foo->{two} = 2;
+
+{
+    no strict 'refs';
+    is(\%{'Foo::foo'}, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the metas');
+
+    ok(exists ${'Foo::foo'}{two}, '... our %foo was updated correctly');
+    is(${'Foo::foo'}{two}, 2, '... our %foo was updated correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding an ARRAY
+
+ok(!defined($Foo::{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(defined($Foo::{bar}), '... the @bar slot was created successfully');
+ok($foo_stash->has_symbol('@bar'), '... the meta agrees');
+
+# ... why does this not work ...
+
+ok(!$foo_stash->has_symbol('$bar'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%bar'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&bar'), '... CODE shouldnt have been created too');
+
+# check the value itself
+
+{
+    no strict 'refs';
+    is(scalar @{'Foo::bar'}, 3, '... our @bar was initialized correctly');
+    is(${'Foo::bar'}[1], 2, '... our @bar was initialized correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding a SCALAR
+
+ok(!defined($Foo::{baz}), '... the $baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('$baz' => 10);
+}, undef, '... created $Foo::baz successfully');
+
+ok(defined($Foo::{baz}), '... the $baz slot was created successfully');
+ok($foo_stash->has_symbol('$baz'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('@baz'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%baz'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&baz'), '... CODE shouldnt have been created too');
+
+is(${$foo_stash->get_symbol('$baz')}, 10, '... got the right value back');
+
+{
+    no strict 'refs';
+    ${'Foo::baz'} = 1;
+
+    is(${'Foo::baz'}, 1, '... our $baz was assigned to correctly');
+    is(${$foo_stash->get_symbol('$baz')}, 1, '... the meta agrees');
+}
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo::{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk" });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo::{funk}), '... the &funk slot was created successfully');
+ok($foo_stash->has_symbol('&funk'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('$funk'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@funk'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%funk'), '... HASH shouldnt have been created too');
+
+{
+    no strict 'refs';
+    ok(defined &{'Foo::funk'}, '... our &funk exists');
+}
+
+is(Foo->funk(), 'Foo::funk', '... got the right value from the function');
+
+# ----------------------------------------------------------------------
+## test multiple slots in the glob
+
+my $ARRAY = [ 1, 2, 3 ];
+my $CODE = sub { "Foo::foo" };
+
+is(exception {
+    $foo_stash->add_symbol('@foo' => $ARRAY);
+}, undef, '... created @Foo::foo successfully');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot was added successfully');
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('&foo' => $CODE);
+}, undef, '... created &Foo::foo successfully');
+
+ok($foo_stash->has_symbol('&foo'), '... the meta agrees');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('$foo' => 'Foo::foo');
+}, undef, '... created $Foo::foo successfully');
+
+ok($foo_stash->has_symbol('$foo'), '... the meta agrees');
+my $SCALAR = $foo_stash->get_symbol('$foo');
+is($$SCALAR, 'Foo::foo', '... got the right scalar value back');
+
+{
+    no strict 'refs';
+    is(${'Foo::foo'}, 'Foo::foo', '... got the right value from the scalar');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('%foo');
+}, undef, '... removed %Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('%foo'), '... the %foo slot was removed successfully');
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('&foo'), '... the &foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(*{"Foo::foo"}{CODE}), '... the &foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('&foo');
+}, undef, '... removed &Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('&foo'), '... the &foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('$foo');
+}, undef, '... removed $Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('$foo'), '... the $foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(!defined(${"Foo::foo"}), '... the $foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols;
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols('CODE');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('CODE') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('&' . $symbol), '... got the right symbol');
+    }
+}
+
+{
+    $foo_stash->add_symbol('%bare');
+    ok(!$foo_stash->has_symbol('$bare'),
+       "add_symbol with single argument doesn't vivify scalar slot");
+}
+
+{
+    $foo_stash->add_symbol('%zork', {});
+
+    my $syms = $foo_stash->get_all_symbols('HASH');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('HASH') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('%' . $symbol), '... got the right symbol');
+    }
+
+    is_deeply(
+        $syms,
+        {
+            zork => *{ $Foo::{zork} }{HASH},
+            bare => *{ $Foo::{bare} }{HASH},
+        },
+        "got the right ones",
+    );
+}
+
+# check some errors
+
+like(exception {
+    $foo_stash->add_symbol('@bar', {})
+}, qr/HASH.*is not of type ARRAY/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('bar', [])
+}, qr/ARRAY.*is not of type IO/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', sub { })
+}, qr/CODE.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+{
+    package Bar;
+    open *foo, '<', $0;
+}
+
+like(exception {
+    $foo_stash->add_symbol('$bar', *Bar::foo{IO})
+}, qr/IO.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+# check compile time manipulation
+
+{
+    package Baz;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+    BEGIN { Package::Stash->new(__PACKAGE__)->remove_symbol('&foo') }
+}
+
+{
+    my $stash = Package::Stash->new('Baz');
+    is(${ $stash->get_symbol('$foo') }, 23, "got \$foo");
+    is_deeply($stash->get_symbol('@foo'), ['bar'], "got \@foo");
+    is_deeply($stash->get_symbol('%foo'), {baz => 1}, "got \%foo");
+    ok(!$stash->has_symbol('&foo'), "got \&foo");
+    is($stash->get_symbol('foo'), *Baz::foo{IO}, "got foo");
+}
+
+{
+    package Quux;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+}
+
+{
+    my $stash = Package::Stash->new('Quux');
+
+    my %expect = (
+        '$foo' => \23,
+        '@foo' => ["bar"],
+        '%foo' => { baz => 1 },
+        '&foo' => \&Quux::foo,
+        'foo'  => *Quux::foo{IO},
+    );
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 42});
+
+    $expect{'%bar'} = {x => 42};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 43});
+
+    $expect{'%bar'} = {x => 43};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+}
+
+{
+    package Quuux;
+    our $foo;
+    our @foo;
+    our @bar;
+    our %baz;
+    sub baz { }
+    use constant quux => 1;
+    use constant quuux => [];
+    sub quuuux;
+}
+
+{
+    my $quuux = Package::Stash->new('Quuux');
+    is_deeply(
+        [sort $quuux->list_all_symbols],
+        [qw(BEGIN bar baz foo quuuux quuux quux)],
+        "list_all_symbols",
+    );
+    { local $TODO = $] < 5.010
+          ? "undef scalars aren't visible on 5.8"
+          : undef;
+    is_deeply(
+        [sort $quuux->list_all_symbols('SCALAR')],
+        [qw(foo)],
+        "list_all_symbols SCALAR",
+    );
+    }
+    is_deeply(
+        [sort $quuux->list_all_symbols('ARRAY')],
+        [qw(bar foo)],
+        "list_all_symbols ARRAY",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('HASH')],
+        [qw(baz)],
+        "list_all_symbols HASH",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('CODE')],
+        [qw(baz quuuux quuux quux)],
+        "list_all_symbols CODE",
+    );
+}
+
+for my $package ('Foo:Bar', 'Foo/Bar', 'Foo Bar', 'Foo:::Bar', '') {
+    like(
+        exception { Package::Stash->new($package) },
+        qr/^$package is not a module name/,
+        "$package is not a module name"
+    );
+}
+
+like(
+    exception { Package::Stash->new([]) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+like(
+    exception { Package::Stash->new(undef) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+done_testing;
diff --git a/t/compile-time.t b/t/compile-time.t
new file mode 100644 (file)
index 0000000..170df48
--- /dev/null
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use_ok('CompileTime');
+
+done_testing;
diff --git a/t/edge-cases.t b/t/edge-cases.t
new file mode 100644 (file)
index 0000000..7b85c16
--- /dev/null
@@ -0,0 +1,112 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use Package::Stash;
+
+{
+    package Foo;
+    use constant FOO => 1;
+    use constant BAR => \1;
+    use constant BAZ => [];
+    use constant QUUX => {};
+    use constant QUUUX => sub { };
+    sub normal { }
+    sub stub;
+    sub normal_with_proto () { }
+    sub stub_with_proto ();
+
+    our $SCALAR;
+    our $SCALAR_WITH_VALUE = 1;
+    our @ARRAY;
+    our %HASH;
+}
+
+my $stash = Package::Stash->new('Foo');
+{ local $TODO = $] < 5.010
+      ? "undef scalars aren't visible on 5.8"
+      : undef;
+ok($stash->has_symbol('$SCALAR'), '$SCALAR');
+}
+ok($stash->has_symbol('$SCALAR_WITH_VALUE'), '$SCALAR_WITH_VALUE');
+ok($stash->has_symbol('@ARRAY'), '@ARRAY');
+ok($stash->has_symbol('%HASH'), '%HASH');
+is_deeply(
+    [sort $stash->list_all_symbols('CODE')],
+    [qw(BAR BAZ FOO QUUUX QUUX normal normal_with_proto stub stub_with_proto)],
+    "can see all code symbols"
+);
+
+$stash->add_symbol('%added', {});
+ok(!$stash->has_symbol('$added'), '$added');
+ok(!$stash->has_symbol('@added'), '@added');
+ok($stash->has_symbol('%added'), '%added');
+
+my $constant = $stash->get_symbol('&FOO');
+is(ref($constant), 'CODE', "expanded a constant into a coderef");
+
+# ensure get doesn't prevent subsequent vivification (not sure what the deal
+# was here)
+is(ref($stash->get_symbol('$glob')), '', "nothing yet");
+is(ref($stash->get_or_add_symbol('$glob')), 'SCALAR', "got an empty scalar");
+
+SKIP: {
+    skip "PP doesn't support anon stashes before 5.14", 4
+        if $] < 5.014 && $Package::Stash::IMPLEMENTATION eq 'PP';
+    skip "XS doesn't support anon stashes before 5.10", 4
+        if $] < 5.010 && $Package::Stash::IMPLEMENTATION eq 'XS';
+    local $TODO = "don't know how to properly inflate a stash entry in PP"
+        if $Package::Stash::IMPLEMENTATION eq 'PP';
+
+    my $anon = {}; # not using Package::Anon
+    $anon->{foo} = -1;     # stub
+    $anon->{bar} = '$&';   # stub with prototype
+    $anon->{baz} = \"foo"; # constant
+
+    my $stash = Package::Stash->new($anon);
+    is(
+        exception {
+            is(ref($stash->get_symbol('&foo')), 'CODE',
+               "stub expanded into a glob");
+            is(ref($stash->get_symbol('&bar')), 'CODE',
+               "stub with prototype expanded into a glob");
+            is(ref($stash->get_symbol('&baz')), 'CODE',
+               "constant expanded into a glob");
+        },
+        undef,
+        "can call get_symbol on weird stash entries"
+    );
+}
+
+{
+    my $warning;
+    local $SIG{__WARN__} = sub { $warning = $_[0] };
+    my $stash = Package::Stash->new('Bar');
+    $stash->add_symbol('&foo' => sub { });
+    $stash->add_symbol('&foo' => sub { });
+    is($warning, undef, "no redefinition warnings");
+}
+
+{
+    local $TODO = $] < 5.010
+        ? "undef scalars aren't visible on 5.8"
+        : undef;
+    my $stash = Package::Stash->new('Baz');
+    $stash->add_symbol('$baz', \undef);
+    ok($stash->has_symbol('$baz'), "immortal scalars are also visible");
+}
+
+{
+    {
+        package HasISA::Super;
+        package HasISA;
+        our @ISA = ('HasISA::Super');
+    }
+    ok(HasISA->isa('HasISA::Super'));
+    my $stash = Package::Stash->new('HasISA');
+    is_deeply([$stash->list_all_symbols('SCALAR')], []);
+}
+
+done_testing;
diff --git a/t/extension.t b/t/extension.t
new file mode 100644 (file)
index 0000000..d9812cd
--- /dev/null
@@ -0,0 +1,75 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+{
+    package My::Package::Stash;
+    use strict;
+    use warnings;
+
+    use base 'Package::Stash';
+
+    use Symbol 'gensym';
+
+    sub new {
+        my $class = shift;
+        my $self = $class->SUPER::new(@_);
+        $self->{namespace} = {};
+        return $self;
+    }
+
+    sub namespace { shift->{namespace} }
+
+    sub add_symbol {
+        my ($self, $variable, $initial_value) = @_;
+
+        (my $name = $variable) =~ s/^[\$\@\%\&]//;
+
+        my $glob = gensym();
+        *{$glob} = $initial_value if defined $initial_value;
+        $self->namespace->{$name} = *{$glob};
+    }
+}
+
+# No actually package Foo exists :)
+my $foo_stash = My::Package::Stash->new('Foo');
+
+isa_ok($foo_stash, 'My::Package::Stash');
+isa_ok($foo_stash, 'Package::Stash');
+
+ok(!defined($Foo::{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the foo_stash agrees');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... the %foo symbol is created succcessfully');
+
+ok(!defined($Foo::{foo}), '... the %foo slot has not been created in the actual Foo package');
+ok($foo_stash->has_symbol('%foo'), '... the foo_stash agrees');
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+$foo->{two} = 2;
+
+is($foo, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the foo_stashs');
+
+ok(!defined($Foo::{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(!defined($Foo::{bar}), '... the @bar slot has still not been created');
+
+ok(!defined($Foo::{baz}), '... the %baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('%baz');
+}, undef, '... created %Foo::baz successfully');
+
+ok(!defined($Foo::{baz}), '... the %baz slot has still not been created');
+
+done_testing;
diff --git a/t/get.t b/t/get.t
new file mode 100644 (file)
index 0000000..fdf6f22
--- /dev/null
+++ b/t/get.t
@@ -0,0 +1,185 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+use Scalar::Util;
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Hash');
+        my $val = $stash->get_symbol('%foo');
+        is($val, undef, "got nothing yet");
+    }
+    {
+        no warnings 'void', 'once';
+        %Hash::foo;
+    }
+    BEGIN {
+        my $stash = Package::Stash->new('Hash');
+        my $val = $stash->get_symbol('%foo');
+        is(ref($val), 'HASH', "got something");
+        $val->{bar} = 1;
+        is_deeply($stash->get_symbol('%foo'), {bar => 1},
+                  "got the right variable");
+        is_deeply(\%Hash::foo, {bar => 1},
+                  "stash has the right variable");
+    }
+}
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Array');
+        my $val = $stash->get_symbol('@foo');
+        is($val, undef, "got nothing yet");
+    }
+    {
+        no warnings 'void', 'once';
+        @Array::foo;
+    }
+    BEGIN {
+        my $stash = Package::Stash->new('Array');
+        my $val = $stash->get_symbol('@foo');
+        is(ref($val), 'ARRAY', "got something");
+        push @$val, 1;
+        is_deeply($stash->get_symbol('@foo'), [1],
+                  "got the right variable");
+        is_deeply(\@Array::foo, [1],
+                  "stash has the right variable");
+    }
+}
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Scalar');
+        my $val = $stash->get_symbol('$foo');
+        is($val, undef, "got nothing yet");
+    }
+    {
+        no warnings 'void', 'once';
+        $Scalar::foo;
+    }
+    BEGIN {
+        my $stash = Package::Stash->new('Scalar');
+        my $val = $stash->get_symbol('$foo');
+        is(ref($val), 'SCALAR', "got something");
+        $$val = 1;
+        is_deeply($stash->get_symbol('$foo'), \1,
+                  "got the right variable");
+        is($Scalar::foo, 1,
+           "stash has the right variable");
+    }
+}
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Code');
+        my $val = $stash->get_symbol('&foo');
+        is($val, undef, "got nothing yet");
+    }
+    {
+        no warnings 'void', 'once';
+        sub Code::foo { }
+    }
+    BEGIN {
+        my $stash = Package::Stash->new('Code');
+        my $val = $stash->get_symbol('&foo');
+        is(ref($val), 'CODE', "got something");
+        is(prototype($val), undef, "got the right variable");
+        &Scalar::Util::set_prototype($val, '&');
+        is($stash->get_symbol('&foo'), $val,
+           "got the right variable");
+        is(prototype($stash->get_symbol('&foo')), '&',
+           "got the right variable");
+        is(prototype(\&Code::foo), '&',
+           "stash has the right variable");
+    }
+}
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Io');
+        my $val = $stash->get_symbol('FOO');
+        is($val, undef, "got nothing yet");
+    }
+    {
+        no warnings 'void', 'once';
+        package Io;
+        fileno(FOO);
+    }
+    BEGIN {
+        my $stash = Package::Stash->new('Io');
+        my $val = $stash->get_symbol('FOO');
+        isa_ok($val, 'IO');
+        my $str = "foo";
+        open $val, '<', \$str;
+        is(readline($stash->get_symbol('FOO')), "foo",
+           "got the right variable");
+        seek($stash->get_symbol('FOO'), 0, 0);
+        {
+            package Io;
+            ::isa_ok(*FOO{IO}, 'IO');
+            ::is(<FOO>, "foo",
+                 "stash has the right variable");
+        }
+    }
+}
+
+{
+    my $stash = Package::Stash->new('Hash::Vivify');
+    my $val = $stash->get_or_add_symbol('%foo');
+    is(ref($val), 'HASH', "got something");
+    $val->{bar} = 1;
+    is_deeply($stash->get_or_add_symbol('%foo'), {bar => 1},
+              "got the right variable");
+    no warnings 'once';
+    is_deeply(\%Hash::Vivify::foo, {bar => 1},
+              "stash has the right variable");
+}
+
+{
+    my $stash = Package::Stash->new('Array::Vivify');
+    my $val = $stash->get_or_add_symbol('@foo');
+    is(ref($val), 'ARRAY', "got something");
+    push @$val, 1;
+    is_deeply($stash->get_or_add_symbol('@foo'), [1],
+              "got the right variable");
+    no warnings 'once';
+    is_deeply(\@Array::Vivify::foo, [1],
+              "stash has the right variable");
+}
+
+{
+    my $stash = Package::Stash->new('Scalar::Vivify');
+    my $val = $stash->get_or_add_symbol('$foo');
+    is(ref($val), 'SCALAR', "got something");
+    $$val = 1;
+    is_deeply($stash->get_or_add_symbol('$foo'), \1,
+              "got the right variable");
+    no warnings 'once';
+    is($Scalar::Vivify::foo, 1,
+       "stash has the right variable");
+}
+
+{
+    BEGIN {
+        my $stash = Package::Stash->new('Io::Vivify');
+        my $val = $stash->get_or_add_symbol('FOO');
+        isa_ok($val, 'IO');
+        my $str = "foo";
+        open $val, '<', \$str;
+        is(readline($stash->get_symbol('FOO')), "foo",
+           "got the right variable");
+        seek($stash->get_symbol('FOO'), 0, 0);
+    }
+    {
+        package Io::Vivify;
+        no warnings 'once';
+        ::isa_ok(*FOO{IO}, 'IO');
+        ::is(<FOO>, "foo",
+             "stash has the right variable");
+    }
+}
+
+done_testing;
diff --git a/t/impl-selection/basic-pp.t b/t/impl-selection/basic-pp.t
new file mode 100644 (file)
index 0000000..7db4fa5
--- /dev/null
@@ -0,0 +1,451 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+
+BEGIN { $Package::Stash::IMPLEMENTATION = 'PP' }
+
+use Package::Stash;
+
+ok(exists $INC{'Package/Stash/PP.pm'}, "loaded PP");
+ok(!exists $INC{'Package/Stash/XS.pm'}, "didn't load XS");
+
+like(exception { Package::Stash->name }, qr/Can't call name as a class method/,
+   q{... can't call name() as a class method});
+
+{
+    package Foo;
+
+    use constant SOME_CONSTANT => 1;
+}
+
+# ----------------------------------------------------------------------
+## tests adding a HASH
+
+my $foo_stash = Package::Stash->new('Foo');
+ok(!defined($Foo::{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the object agrees');
+ok(!defined($Foo::{foo}), '... checking doesn\' vivify');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... created %Foo::foo successfully');
+
+# ... scalar should NOT be created here
+
+ok(!$foo_stash->has_symbol('$foo'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@foo'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&foo'), '... CODE shouldnt have been created too');
+
+ok(defined($Foo::{foo}), '... the %foo slot was created successfully');
+ok($foo_stash->has_symbol('%foo'), '... the meta agrees');
+
+# check the value ...
+
+{
+    no strict 'refs';
+    ok(exists ${'Foo::foo'}{one}, '... our %foo was initialized correctly');
+    is(${'Foo::foo'}{one}, 1, '... our %foo was initialized correctly');
+}
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+# ... make sure changes propogate up
+
+$foo->{two} = 2;
+
+{
+    no strict 'refs';
+    is(\%{'Foo::foo'}, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the metas');
+
+    ok(exists ${'Foo::foo'}{two}, '... our %foo was updated correctly');
+    is(${'Foo::foo'}{two}, 2, '... our %foo was updated correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding an ARRAY
+
+ok(!defined($Foo::{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(defined($Foo::{bar}), '... the @bar slot was created successfully');
+ok($foo_stash->has_symbol('@bar'), '... the meta agrees');
+
+# ... why does this not work ...
+
+ok(!$foo_stash->has_symbol('$bar'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%bar'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&bar'), '... CODE shouldnt have been created too');
+
+# check the value itself
+
+{
+    no strict 'refs';
+    is(scalar @{'Foo::bar'}, 3, '... our @bar was initialized correctly');
+    is(${'Foo::bar'}[1], 2, '... our @bar was initialized correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding a SCALAR
+
+ok(!defined($Foo::{baz}), '... the $baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('$baz' => 10);
+}, undef, '... created $Foo::baz successfully');
+
+ok(defined($Foo::{baz}), '... the $baz slot was created successfully');
+ok($foo_stash->has_symbol('$baz'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('@baz'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%baz'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&baz'), '... CODE shouldnt have been created too');
+
+is(${$foo_stash->get_symbol('$baz')}, 10, '... got the right value back');
+
+{
+    no strict 'refs';
+    ${'Foo::baz'} = 1;
+
+    is(${'Foo::baz'}, 1, '... our $baz was assigned to correctly');
+    is(${$foo_stash->get_symbol('$baz')}, 1, '... the meta agrees');
+}
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo::{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk" });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo::{funk}), '... the &funk slot was created successfully');
+ok($foo_stash->has_symbol('&funk'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('$funk'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@funk'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%funk'), '... HASH shouldnt have been created too');
+
+{
+    no strict 'refs';
+    ok(defined &{'Foo::funk'}, '... our &funk exists');
+}
+
+is(Foo->funk(), 'Foo::funk', '... got the right value from the function');
+
+# ----------------------------------------------------------------------
+## test multiple slots in the glob
+
+my $ARRAY = [ 1, 2, 3 ];
+my $CODE = sub { "Foo::foo" };
+
+is(exception {
+    $foo_stash->add_symbol('@foo' => $ARRAY);
+}, undef, '... created @Foo::foo successfully');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot was added successfully');
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('&foo' => $CODE);
+}, undef, '... created &Foo::foo successfully');
+
+ok($foo_stash->has_symbol('&foo'), '... the meta agrees');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('$foo' => 'Foo::foo');
+}, undef, '... created $Foo::foo successfully');
+
+ok($foo_stash->has_symbol('$foo'), '... the meta agrees');
+my $SCALAR = $foo_stash->get_symbol('$foo');
+is($$SCALAR, 'Foo::foo', '... got the right scalar value back');
+
+{
+    no strict 'refs';
+    is(${'Foo::foo'}, 'Foo::foo', '... got the right value from the scalar');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('%foo');
+}, undef, '... removed %Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('%foo'), '... the %foo slot was removed successfully');
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('&foo'), '... the &foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(*{"Foo::foo"}{CODE}), '... the &foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('&foo');
+}, undef, '... removed &Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('&foo'), '... the &foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('$foo');
+}, undef, '... removed $Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('$foo'), '... the $foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(!defined(${"Foo::foo"}), '... the $foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols;
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols('CODE');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('CODE') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('&' . $symbol), '... got the right symbol');
+    }
+}
+
+{
+    $foo_stash->add_symbol('%bare');
+    ok(!$foo_stash->has_symbol('$bare'),
+       "add_symbol with single argument doesn't vivify scalar slot");
+}
+
+{
+    $foo_stash->add_symbol('%zork', {});
+
+    my $syms = $foo_stash->get_all_symbols('HASH');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('HASH') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('%' . $symbol), '... got the right symbol');
+    }
+
+    is_deeply(
+        $syms,
+        {
+            zork => *{ $Foo::{zork} }{HASH},
+            bare => *{ $Foo::{bare} }{HASH},
+        },
+        "got the right ones",
+    );
+}
+
+# check some errors
+
+like(exception {
+    $foo_stash->add_symbol('@bar', {})
+}, qr/HASH.*is not of type ARRAY/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('bar', [])
+}, qr/ARRAY.*is not of type IO/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', sub { })
+}, qr/CODE.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+{
+    package Bar;
+    open *foo, '<', $0;
+}
+
+like(exception {
+    $foo_stash->add_symbol('$bar', *Bar::foo{IO})
+}, qr/IO.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+# check compile time manipulation
+
+{
+    package Baz;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+    BEGIN { Package::Stash->new(__PACKAGE__)->remove_symbol('&foo') }
+}
+
+{
+    my $stash = Package::Stash->new('Baz');
+    is(${ $stash->get_symbol('$foo') }, 23, "got \$foo");
+    is_deeply($stash->get_symbol('@foo'), ['bar'], "got \@foo");
+    is_deeply($stash->get_symbol('%foo'), {baz => 1}, "got \%foo");
+    ok(!$stash->has_symbol('&foo'), "got \&foo");
+    is($stash->get_symbol('foo'), *Baz::foo{IO}, "got foo");
+}
+
+{
+    package Quux;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+}
+
+{
+    my $stash = Package::Stash->new('Quux');
+
+    my %expect = (
+        '$foo' => \23,
+        '@foo' => ["bar"],
+        '%foo' => { baz => 1 },
+        '&foo' => \&Quux::foo,
+        'foo'  => *Quux::foo{IO},
+    );
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 42});
+
+    $expect{'%bar'} = {x => 42};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 43});
+
+    $expect{'%bar'} = {x => 43};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+}
+
+{
+    package Quuux;
+    our $foo;
+    our @foo;
+    our @bar;
+    our %baz;
+    sub baz { }
+    use constant quux => 1;
+    use constant quuux => [];
+    sub quuuux;
+}
+
+{
+    my $quuux = Package::Stash->new('Quuux');
+    is_deeply(
+        [sort $quuux->list_all_symbols],
+        [qw(BEGIN bar baz foo quuuux quuux quux)],
+        "list_all_symbols",
+    );
+    { local $TODO = $] < 5.010
+          ? "undef scalars aren't visible on 5.8"
+          : undef;
+    is_deeply(
+        [sort $quuux->list_all_symbols('SCALAR')],
+        [qw(foo)],
+        "list_all_symbols SCALAR",
+    );
+    }
+    is_deeply(
+        [sort $quuux->list_all_symbols('ARRAY')],
+        [qw(bar foo)],
+        "list_all_symbols ARRAY",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('HASH')],
+        [qw(baz)],
+        "list_all_symbols HASH",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('CODE')],
+        [qw(baz quuuux quuux quux)],
+        "list_all_symbols CODE",
+    );
+}
+
+for my $package ('Foo:Bar', 'Foo/Bar', 'Foo Bar', 'Foo:::Bar', '') {
+    like(
+        exception { Package::Stash->new($package) },
+        qr/^$package is not a module name/,
+        "$package is not a module name"
+    );
+}
+
+like(
+    exception { Package::Stash->new([]) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+like(
+    exception { Package::Stash->new(undef) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+done_testing;
diff --git a/t/impl-selection/basic-xs.t b/t/impl-selection/basic-xs.t
new file mode 100644 (file)
index 0000000..935b4b6
--- /dev/null
@@ -0,0 +1,452 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+use Test::Needs 'Package::Stash::XS';
+
+BEGIN { $Package::Stash::IMPLEMENTATION = 'XS' }
+
+use Package::Stash;
+
+ok(exists $INC{'Package/Stash/XS.pm'}, "loaded XS");
+ok(!exists $INC{'Package/Stash/PP.pm'}, "didn't load PP");
+
+like(exception { Package::Stash->name }, qr/Can't call name as a class method/,
+   q{... can't call name() as a class method});
+
+{
+    package Foo;
+
+    use constant SOME_CONSTANT => 1;
+}
+
+# ----------------------------------------------------------------------
+## tests adding a HASH
+
+my $foo_stash = Package::Stash->new('Foo');
+ok(!defined($Foo::{foo}), '... the %foo slot has not been created yet');
+ok(!$foo_stash->has_symbol('%foo'), '... the object agrees');
+ok(!defined($Foo::{foo}), '... checking doesn\' vivify');
+
+is(exception {
+    $foo_stash->add_symbol('%foo' => { one => 1 });
+}, undef, '... created %Foo::foo successfully');
+
+# ... scalar should NOT be created here
+
+ok(!$foo_stash->has_symbol('$foo'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@foo'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&foo'), '... CODE shouldnt have been created too');
+
+ok(defined($Foo::{foo}), '... the %foo slot was created successfully');
+ok($foo_stash->has_symbol('%foo'), '... the meta agrees');
+
+# check the value ...
+
+{
+    no strict 'refs';
+    ok(exists ${'Foo::foo'}{one}, '... our %foo was initialized correctly');
+    is(${'Foo::foo'}{one}, 1, '... our %foo was initialized correctly');
+}
+
+my $foo = $foo_stash->get_symbol('%foo');
+is_deeply({ one => 1 }, $foo, '... got the right package variable back');
+
+# ... make sure changes propogate up
+
+$foo->{two} = 2;
+
+{
+    no strict 'refs';
+    is(\%{'Foo::foo'}, $foo_stash->get_symbol('%foo'), '... our %foo is the same as the metas');
+
+    ok(exists ${'Foo::foo'}{two}, '... our %foo was updated correctly');
+    is(${'Foo::foo'}{two}, 2, '... our %foo was updated correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding an ARRAY
+
+ok(!defined($Foo::{bar}), '... the @bar slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('@bar' => [ 1, 2, 3 ]);
+}, undef, '... created @Foo::bar successfully');
+
+ok(defined($Foo::{bar}), '... the @bar slot was created successfully');
+ok($foo_stash->has_symbol('@bar'), '... the meta agrees');
+
+# ... why does this not work ...
+
+ok(!$foo_stash->has_symbol('$bar'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%bar'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&bar'), '... CODE shouldnt have been created too');
+
+# check the value itself
+
+{
+    no strict 'refs';
+    is(scalar @{'Foo::bar'}, 3, '... our @bar was initialized correctly');
+    is(${'Foo::bar'}[1], 2, '... our @bar was initialized correctly');
+}
+
+# ----------------------------------------------------------------------
+## test adding a SCALAR
+
+ok(!defined($Foo::{baz}), '... the $baz slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('$baz' => 10);
+}, undef, '... created $Foo::baz successfully');
+
+ok(defined($Foo::{baz}), '... the $baz slot was created successfully');
+ok($foo_stash->has_symbol('$baz'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('@baz'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%baz'), '... HASH shouldnt have been created too');
+ok(!$foo_stash->has_symbol('&baz'), '... CODE shouldnt have been created too');
+
+is(${$foo_stash->get_symbol('$baz')}, 10, '... got the right value back');
+
+{
+    no strict 'refs';
+    ${'Foo::baz'} = 1;
+
+    is(${'Foo::baz'}, 1, '... our $baz was assigned to correctly');
+    is(${$foo_stash->get_symbol('$baz')}, 1, '... the meta agrees');
+}
+
+# ----------------------------------------------------------------------
+## test adding a CODE
+
+ok(!defined($Foo::{funk}), '... the &funk slot has not been created yet');
+
+is(exception {
+    $foo_stash->add_symbol('&funk' => sub { "Foo::funk" });
+}, undef, '... created &Foo::funk successfully');
+
+ok(defined($Foo::{funk}), '... the &funk slot was created successfully');
+ok($foo_stash->has_symbol('&funk'), '... the meta agrees');
+
+ok(!$foo_stash->has_symbol('$funk'), '... SCALAR shouldnt have been created too');
+ok(!$foo_stash->has_symbol('@funk'), '... ARRAY shouldnt have been created too');
+ok(!$foo_stash->has_symbol('%funk'), '... HASH shouldnt have been created too');
+
+{
+    no strict 'refs';
+    ok(defined &{'Foo::funk'}, '... our &funk exists');
+}
+
+is(Foo->funk(), 'Foo::funk', '... got the right value from the function');
+
+# ----------------------------------------------------------------------
+## test multiple slots in the glob
+
+my $ARRAY = [ 1, 2, 3 ];
+my $CODE = sub { "Foo::foo" };
+
+is(exception {
+    $foo_stash->add_symbol('@foo' => $ARRAY);
+}, undef, '... created @Foo::foo successfully');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot was added successfully');
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('&foo' => $CODE);
+}, undef, '... created &Foo::foo successfully');
+
+ok($foo_stash->has_symbol('&foo'), '... the meta agrees');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+
+is(exception {
+    $foo_stash->add_symbol('$foo' => 'Foo::foo');
+}, undef, '... created $Foo::foo successfully');
+
+ok($foo_stash->has_symbol('$foo'), '... the meta agrees');
+my $SCALAR = $foo_stash->get_symbol('$foo');
+is($$SCALAR, 'Foo::foo', '... got the right scalar value back');
+
+{
+    no strict 'refs';
+    is(${'Foo::foo'}, 'Foo::foo', '... got the right value from the scalar');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('%foo');
+}, undef, '... removed %Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('%foo'), '... the %foo slot was removed successfully');
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('&foo'), '... the &foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('&foo'), $CODE, '... got the right value for &Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(*{"Foo::foo"}{CODE}), '... the &foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('&foo');
+}, undef, '... removed &Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('&foo'), '... the &foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+ok($foo_stash->has_symbol('$foo'), '... the $foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+is($foo_stash->get_symbol('$foo'), $SCALAR, '... got the right value for $Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+    ok(defined(${"Foo::foo"}), '... the $foo slot has NOT been removed');
+}
+
+is(exception {
+    $foo_stash->remove_symbol('$foo');
+}, undef, '... removed $Foo::foo successfully');
+
+ok(!$foo_stash->has_symbol('$foo'), '... the $foo slot no longer exists');
+
+ok($foo_stash->has_symbol('@foo'), '... the @foo slot still exists');
+
+is($foo_stash->get_symbol('@foo'), $ARRAY, '... got the right values for @Foo::foo');
+
+{
+    no strict 'refs';
+    ok(!defined(*{"Foo::foo"}{HASH}), '... the %foo slot has been removed successfully');
+    ok(!defined(*{"Foo::foo"}{CODE}), '... the &foo slot has now been removed');
+    ok(!defined(${"Foo::foo"}), '... the $foo slot has now been removed');
+    ok(defined(*{"Foo::foo"}{ARRAY}), '... the @foo slot has NOT been removed');
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols;
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+}
+
+{
+    my $syms = $foo_stash->get_all_symbols('CODE');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('CODE') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('&' . $symbol), '... got the right symbol');
+    }
+}
+
+{
+    $foo_stash->add_symbol('%bare');
+    ok(!$foo_stash->has_symbol('$bare'),
+       "add_symbol with single argument doesn't vivify scalar slot");
+}
+
+{
+    $foo_stash->add_symbol('%zork', {});
+
+    my $syms = $foo_stash->get_all_symbols('HASH');
+
+    is_deeply(
+        [ sort keys %{ $syms } ],
+        [ sort $foo_stash->list_all_symbols('HASH') ],
+        '... the fetched symbols are the same as the listed ones'
+    );
+
+    foreach my $symbol (keys %{ $syms }) {
+        is($syms->{$symbol}, $foo_stash->get_symbol('%' . $symbol), '... got the right symbol');
+    }
+
+    is_deeply(
+        $syms,
+        {
+            zork => *{ $Foo::{zork} }{HASH},
+            bare => *{ $Foo::{bare} }{HASH},
+        },
+        "got the right ones",
+    );
+}
+
+# check some errors
+
+like(exception {
+    $foo_stash->add_symbol('@bar', {})
+}, qr/HASH.*is not of type ARRAY/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('bar', [])
+}, qr/ARRAY.*is not of type IO/, "can't initialize a slot with the wrong type of value");
+
+like(exception {
+    $foo_stash->add_symbol('$bar', sub { })
+}, qr/CODE.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+{
+    package Bar;
+    open *foo, '<', $0;
+}
+
+like(exception {
+    $foo_stash->add_symbol('$bar', *Bar::foo{IO})
+}, qr/IO.*is not of type SCALAR/, "can't initialize a slot with the wrong type of value");
+
+# check compile time manipulation
+
+{
+    package Baz;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+    BEGIN { Package::Stash->new(__PACKAGE__)->remove_symbol('&foo') }
+}
+
+{
+    my $stash = Package::Stash->new('Baz');
+    is(${ $stash->get_symbol('$foo') }, 23, "got \$foo");
+    is_deeply($stash->get_symbol('@foo'), ['bar'], "got \@foo");
+    is_deeply($stash->get_symbol('%foo'), {baz => 1}, "got \%foo");
+    ok(!$stash->has_symbol('&foo'), "got \&foo");
+    is($stash->get_symbol('foo'), *Baz::foo{IO}, "got foo");
+}
+
+{
+    package Quux;
+
+    our $foo = 23;
+    our @foo = "bar";
+    our %foo = (baz => 1);
+    sub foo { }
+    open *foo, '<', $0;
+}
+
+{
+    my $stash = Package::Stash->new('Quux');
+
+    my %expect = (
+        '$foo' => \23,
+        '@foo' => ["bar"],
+        '%foo' => { baz => 1 },
+        '&foo' => \&Quux::foo,
+        'foo'  => *Quux::foo{IO},
+    );
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 42});
+
+    $expect{'%bar'} = {x => 42};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+
+    $stash->add_symbol('%bar' => {x => 43});
+
+    $expect{'%bar'} = {x => 43};
+
+    for my $sym ( sort keys %expect ) {
+        is_deeply(
+            $stash->get_symbol($sym),
+            $expect{$sym},
+            "got expected value for $sym"
+        );
+    }
+}
+
+{
+    package Quuux;
+    our $foo;
+    our @foo;
+    our @bar;
+    our %baz;
+    sub baz { }
+    use constant quux => 1;
+    use constant quuux => [];
+    sub quuuux;
+}
+
+{
+    my $quuux = Package::Stash->new('Quuux');
+    is_deeply(
+        [sort $quuux->list_all_symbols],
+        [qw(BEGIN bar baz foo quuuux quuux quux)],
+        "list_all_symbols",
+    );
+    { local $TODO = $] < 5.010
+          ? "undef scalars aren't visible on 5.8"
+          : undef;
+    is_deeply(
+        [sort $quuux->list_all_symbols('SCALAR')],
+        [qw(foo)],
+        "list_all_symbols SCALAR",
+    );
+    }
+    is_deeply(
+        [sort $quuux->list_all_symbols('ARRAY')],
+        [qw(bar foo)],
+        "list_all_symbols ARRAY",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('HASH')],
+        [qw(baz)],
+        "list_all_symbols HASH",
+    );
+    is_deeply(
+        [sort $quuux->list_all_symbols('CODE')],
+        [qw(baz quuuux quuux quux)],
+        "list_all_symbols CODE",
+    );
+}
+
+for my $package ('Foo:Bar', 'Foo/Bar', 'Foo Bar', 'Foo:::Bar', '') {
+    like(
+        exception { Package::Stash->new($package) },
+        qr/^$package is not a module name/,
+        "$package is not a module name"
+    );
+}
+
+like(
+    exception { Package::Stash->new([]) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+like(
+    exception { Package::Stash->new(undef) },
+    qr/^Package::Stash->new must be passed the name of the package to access/,
+    "module name must be a string"
+);
+
+done_testing;
diff --git a/t/impl-selection/bug-rt-78272.t b/t/impl-selection/bug-rt-78272.t
new file mode 100644 (file)
index 0000000..59f238a
--- /dev/null
@@ -0,0 +1,38 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Fatal;
+
+# https://rt.cpan.org/Public/Bug/Display.html?id=78272
+my $e = $ENV{PACKAGE_STASH_IMPLEMENTATION} = "PP; exit 1";
+
+like(
+    exception { require Package::Stash },
+    qr/$e is not a valid implementation for Package::Stash/,
+    'Arbitrary code in $ENV throws exception'
+);
+
+like(
+    exception {
+        delete $INC{'Package/Stash.pm'};
+        require Package::Stash;
+    },
+    qr/$e is not a valid implementation for Package::Stash/,
+    'Sanity check: forcing package reload throws the exception again'
+);
+
+is(
+    exception {
+        $ENV{PACKAGE_STASH_IMPLEMENTATION} = "PP";
+        delete $INC{'Package/Stash.pm'};
+        require Package::Stash;
+        new_ok(
+            'Package::Stash' => ['Foo'],
+            'Loaded and able to create instances'
+        );
+    },
+    undef,
+    'Valid $ENV value loads correctly'
+);
+
+done_testing;
diff --git a/t/impl-selection/choice.t b/t/impl-selection/choice.t
new file mode 100644 (file)
index 0000000..a161976
--- /dev/null
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+use Test::More;
+
+my $has_xs = eval "require Package::Stash::XS; 1";
+
+require Package::Stash;
+
+no warnings 'once';
+
+my $expected = $has_xs ? 'XS' : 'PP';
+is($Package::Stash::IMPLEMENTATION, $expected,
+   "autodetected properly: $expected");
+can_ok('Package::Stash', 'new');
+
+done_testing;
diff --git a/t/impl-selection/env.t b/t/impl-selection/env.t
new file mode 100644 (file)
index 0000000..4db7681
--- /dev/null
@@ -0,0 +1,38 @@
+use strict;
+use warnings;
+use Test::More;
+
+# XXX: work around dumb core segfault bug when you delete stashes
+sub get_impl { eval '$Package::Stash::IMPLEMENTATION' }
+sub set_impl { eval '$Package::Stash::IMPLEMENTATION = "' . $_[0] . '"' }
+
+{
+    $ENV{PACKAGE_STASH_IMPLEMENTATION} = 'PP';
+    require Package::Stash;
+    is(get_impl, 'PP', "autodetected properly: PP");
+    can_ok('Package::Stash', 'new');
+}
+
+delete $Package::{'Stash::'};
+delete $INC{'Package/Stash.pm'};
+delete $INC{'Package/Stash/PP.pm'};
+
+SKIP: {
+    skip "no XS", 2 unless eval "require Package::Stash::XS; 1";
+    $ENV{PACKAGE_STASH_IMPLEMENTATION} = 'XS';
+    require Package::Stash;
+    is(get_impl, 'XS', "autodetected properly: XS");
+    can_ok('Package::Stash', 'new');
+}
+
+{
+    delete $Package::{'Stash::'};
+    delete $INC{'Package/Stash.pm'};
+    set_impl('INVALID');
+    $ENV{PACKAGE_STASH_IMPLEMENTATION} = 'PP';
+    require Package::Stash;
+    is(get_impl, 'PP', '$ENV takes precedence over $Package::Stash::IMPLEMENTATION');
+    can_ok('Package::Stash', 'new');
+}
+
+done_testing;
diff --git a/t/impl-selection/var.t b/t/impl-selection/var.t
new file mode 100644 (file)
index 0000000..e025304
--- /dev/null
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+use Test::More;
+
+# XXX: work around dumb core segfault bug when you delete stashes
+sub get_impl { eval '$Package::Stash::IMPLEMENTATION' }
+sub set_impl { eval '$Package::Stash::IMPLEMENTATION = "' . $_[0] . '"' }
+
+{
+    $Package::Stash::IMPLEMENTATION = 'PP';
+    require Package::Stash;
+    is(get_impl, 'PP', "autodetected properly: PP");
+    can_ok('Package::Stash', 'new');
+}
+
+delete $Package::{'Stash::'};
+delete $INC{'Package/Stash.pm'};
+delete $INC{'Package/Stash/PP.pm'};
+
+SKIP: {
+    skip "no XS", 2 unless eval "require Package::Stash::XS; 1";
+    $Package::Stash::IMPLEMENTATION = 'XS';
+    require Package::Stash;
+    is(get_impl, 'XS', "autodetected properly: XS");
+    can_ok('Package::Stash', 'new');
+}
+
+done_testing;
diff --git a/t/io.t b/t/io.t
new file mode 100644 (file)
index 0000000..2a1d0f0
--- /dev/null
+++ b/t/io.t
@@ -0,0 +1,50 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+{
+    package Foo;
+    open *foo, "<", $0;
+
+    sub foo { }
+}
+
+{
+    package Bar;
+    open *bar, "<", $0;
+
+    sub bar { }
+}
+
+use Package::Stash;
+
+{
+    my $stash = Package::Stash->new('Foo');
+    ok($stash->has_symbol('&foo'), "has &foo");
+    ok($stash->has_symbol('foo'), "has foo");
+    $stash->remove_symbol('&foo');
+    ok(!$stash->has_symbol('&foo'), "has &foo");
+    ok($stash->has_symbol('foo'), "has foo");
+}
+
+{
+    my $stash = Package::Stash->new('Bar');
+    ok($stash->has_symbol('&bar'), "has &bar");
+    ok($stash->has_symbol('bar'), "has bar");
+    $stash->remove_symbol('bar');
+    ok($stash->has_symbol('&bar'), "has &bar");
+    ok(!$stash->has_symbol('bar'), "has bar");
+}
+
+{
+    my $stash = Package::Stash->new('Baz');
+    is(exception {
+        $stash->add_symbol('baz', *Foo::foo{IO});
+    }, undef, "can add an IO symbol");
+    ok($stash->has_symbol('baz'), "has baz");
+    is($stash->get_symbol('baz'), *Foo::foo{IO}, "got the right baz");
+}
+
+done_testing;
diff --git a/t/isa.t b/t/isa.t
new file mode 100644 (file)
index 0000000..85d03ea
--- /dev/null
+++ b/t/isa.t
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+{
+    package Foo;
+}
+
+{
+    package Bar;
+    sub bar { }
+}
+
+{
+    my $stash = Package::Stash->new('Foo');
+    my @ISA = ('Bar');
+    @{$stash->get_or_add_symbol('@ISA')} = @ISA;
+    isa_ok('Foo', 'Bar');
+    isa_ok(bless({}, 'Foo'), 'Bar');
+}
+
+{
+    package Baz;
+    sub foo { }
+}
+
+{
+    my $stash = Package::Stash->new('Quux');
+    {
+        my $isa = $stash->get_or_add_symbol('@ISA');
+        @$isa = ('Baz');
+        isa_ok('Quux', 'Baz');
+        isa_ok(bless({}, 'Quux'), 'Baz');
+        ok(Quux->can('foo'));
+    }
+    {
+        my $isa = $stash->get_or_add_symbol('@ISA');
+        @$isa = ('Bar');
+        isa_ok('Quux', 'Bar');
+        isa_ok(bless({}, 'Quux'), 'Bar');
+        ok(Quux->can('bar'));
+    }
+}
+
+done_testing;
diff --git a/t/lib/CompileTime.pm b/t/lib/CompileTime.pm
new file mode 100644 (file)
index 0000000..925bc18
--- /dev/null
@@ -0,0 +1,15 @@
+package CompileTime;
+use strict;
+use warnings;
+
+use Package::Stash;
+
+our $foo = 23;
+
+BEGIN {
+    my $stash = Package::Stash->new(__PACKAGE__);
+    $stash->add_symbol('$bar', $foo);
+    $stash->add_symbol('$baz', $stash->get_symbol('$foo'));
+}
+
+1;
diff --git a/t/lib/Package/Stash.pm b/t/lib/Package/Stash.pm
new file mode 100644 (file)
index 0000000..8f97587
--- /dev/null
@@ -0,0 +1,3 @@
+$Package::Stash::IMPLEMENTATION = 'PP';
+do './lib/Package/Stash.pm' or die $@ || $!;
+1;
diff --git a/t/magic.t b/t/magic.t
new file mode 100644 (file)
index 0000000..6c05f2a
--- /dev/null
+++ b/t/magic.t
@@ -0,0 +1,79 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+# @ISA magic
+{
+    my $Foo = Package::Stash->new('ISAFoo');
+    $Foo->add_symbol('&foo' => sub { });
+
+    my $Bar = Package::Stash->new('ISABar');
+    @{ $Bar->get_or_add_symbol('@ISA') } = ('ISAFoo');
+    can_ok('ISABar', 'foo');
+
+    my $Foo2 = Package::Stash->new('ISAFoo2');
+    $Foo2->add_symbol('&foo2' => sub { });
+    @{ $Bar->get_or_add_symbol('@ISA') } = ('ISAFoo2');
+    can_ok('ISABar', 'foo2');
+    ok(!Bar->can('foo'));
+}
+
+{
+    my $main = Package::Stash->new('main');
+    $main->add_symbol('$"', '-');
+    my @foo = qw(a b c);
+    is(eval q["@foo"], 'a-b-c');
+}
+
+SKIP: {
+    skip "only need to test for magic in the xs version", 10
+        unless $Package::Stash::IMPLEMENTATION eq 'XS';
+    skip "magic stashes require perl 5.10+", 10
+        unless $] >= 5.010;
+    skip "magic stashes require Variable::Magic", 10
+        unless eval { require Variable::Magic; 1 };
+
+    my ($fetch, $store);
+    my $wiz = Variable::Magic::wizard(
+        fetch => sub { $fetch++ },
+        store => sub { $store++ },
+    );
+    Variable::Magic::cast(\%MagicStashTest::, $wiz);
+
+    my $stash = Package::Stash->new('MagicStashTest');
+
+    $fetch = 0;
+    $store = 0;
+    $stash->get_symbol('@foo');
+    is($fetch, 1, "get_symbol fetches (empty slot)");
+    is($store, 0, "get_symbol stores (empty slot)");
+
+    $fetch = 0;
+    $store = 0;
+    $stash->get_or_add_symbol('@bar');
+    is($fetch, 0, "get_or_add_symbol fetches (empty slot)");
+    is($store, 1, "get_or_add_symbol stores (empty slot)");
+
+    $fetch = 0;
+    $store = 0;
+    $stash->add_symbol('@baz', ['baz']);
+    is($fetch, 0, "add_symbol fetches");
+    is($store, 1, "add_symbol stores");
+
+    $fetch = 0;
+    $store = 0;
+    $stash->get_symbol('@baz');
+    is($fetch, 1, "get_symbol fetches (populated slot)");
+    is($store, 0, "get_symbol stores (populated slot)");
+
+    $fetch = 0;
+    $store = 0;
+    $stash->get_or_add_symbol('@baz');
+    is($fetch, 1, "get_or_add_symbol fetches (populated slot)");
+    is($store, 0, "get_or_add_symbol stores (populated slot)");
+}
+
+done_testing;
diff --git a/t/paamayim_nekdotayim.t b/t/paamayim_nekdotayim.t
new file mode 100644 (file)
index 0000000..eb906c9
--- /dev/null
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use Package::Stash;
+
+my $stash = Package::Stash->new('Foo');
+# this segfaulted on the xs version
+like(
+    exception { $stash->add_symbol('@bar::baz') },
+    qr/^Variable names may not contain ::/,
+    "can't add symbol with ::"
+);
+like(
+    exception { $stash->get_symbol('@bar::baz') },
+    qr/^Variable names may not contain ::/,
+    "can't add symbol with ::"
+);
+like(
+    exception { $stash->get_or_add_symbol('@bar::baz') },
+    qr/^Variable names may not contain ::/,
+    "can't add symbol with ::"
+);
+
+done_testing;
diff --git a/t/scalar-values.t b/t/scalar-values.t
new file mode 100644 (file)
index 0000000..d15a44a
--- /dev/null
@@ -0,0 +1,52 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+
+use B;
+use Package::Stash;
+use Scalar::Util qw(reftype);
+use Symbol;
+
+my $Bar = Package::Stash->new('Bar');
+
+my $pviv = 3;
+$pviv =~ s/3/4/;
+isa_ok(B::svref_2object(\$pviv), 'B::PVIV');
+is(exception { $Bar->add_symbol('$pviv', \$pviv) }, undef,
+   "can add PVIV values");
+
+my $pvnv = 4.5;
+$pvnv =~ s/4/5/;
+isa_ok(B::svref_2object(\$pvnv), 'B::PVNV');
+is(exception { $Bar->add_symbol('$pvnv', \$pvnv) }, undef,
+   "can add PVNV values");
+
+my $pvmg = "foo";
+bless \$pvmg, 'Foo';
+isa_ok(B::svref_2object(\$pvmg), 'B::PVMG');
+is(exception { $Bar->add_symbol('$pvmg', \$pvmg) }, undef,
+   "can add PVMG values");
+
+my $regexp = qr/foo/;
+isa_ok(B::svref_2object($regexp), ($] < 5.012 ? 'B::PVMG' : 'B::REGEXP'));
+is(exception { $Bar->add_symbol('$regexp', $regexp) }, undef,
+   "can add REGEXP values");
+
+my $pvgv = Symbol::gensym;
+isa_ok(B::svref_2object($pvgv), 'B::GV');
+isnt(exception { $Bar->add_symbol('$pvgv', $pvgv) }, undef,
+     "can't add PVGV values");
+
+my $pvlv = "foo";
+isa_ok(B::svref_2object(\substr($pvlv, 0, 1)), 'B::PVLV');
+is(exception { $Bar->add_symbol('$pvlv', \substr($pvlv, 0, 1)) }, undef,
+   "can add PVLV values");
+
+my $vstring = v1.2.3;
+is(reftype(\$vstring), ($] < 5.010 ? 'SCALAR' : 'VSTRING'));
+is(exception { $Bar->add_symbol('$vstring', \$vstring) }, undef,
+   "can add vstring values");
+
+done_testing;
diff --git a/t/stash-deletion.t b/t/stash-deletion.t
new file mode 100644 (file)
index 0000000..37c6ca4
--- /dev/null
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+{
+    package Gets::Deleted;
+    sub bar { }
+}
+
+{
+    my $delete = Package::Stash->new('Gets::Deleted');
+    ok($delete->has_symbol('&bar'), "sees the method");
+    {
+        no strict 'refs';
+        delete ${'main::Gets::'}{'Deleted::'};
+    }
+    ok(!$delete->has_symbol('&bar'), "method goes away when stash is deleted");
+}
+
+done_testing;
diff --git a/t/synopsis.t b/t/synopsis.t
new file mode 100644 (file)
index 0000000..62a13cd
--- /dev/null
@@ -0,0 +1,18 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+my $stash = Package::Stash->new('Foo');
+$stash->add_symbol('%foo', {bar => 1});
+{
+    no warnings 'once';
+    is($Foo::foo{bar}, 1, "set in the stash properly");
+}
+ok(!$stash->has_symbol('$foo'), "doesn't have anything in scalar slot");
+my $namespace = $stash->namespace;
+is_deeply(*{ $namespace->{foo} }{HASH}, {bar => 1}, "namespace works properly");
+
+done_testing;
diff --git a/t/warnings-taint.t b/t/warnings-taint.t
new file mode 100644 (file)
index 0000000..68dee3c
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env perl -T
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+my $warnings;
+BEGIN {
+    $warnings = '';
+    $SIG{__WARN__} = sub { $warnings .= $_[0] };
+}
+
+BEGIN {
+    my $stash = Package::Stash->new('Foo');
+    $stash->get_or_add_symbol('$bar');
+}
+
+is($warnings, '');
+
+done_testing;
diff --git a/t/warnings.t b/t/warnings.t
new file mode 100644 (file)
index 0000000..965fe60
--- /dev/null
@@ -0,0 +1,21 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+
+use Package::Stash;
+
+my $warnings;
+BEGIN {
+    $warnings = '';
+    $SIG{__WARN__} = sub { $warnings .= $_[0] };
+}
+
+BEGIN {
+    my $stash = Package::Stash->new('Foo');
+    $stash->get_or_add_symbol('$bar');
+}
+
+is($warnings, '');
+
+done_testing;
diff --git a/t/zzz-check-breaks.t b/t/zzz-check-breaks.t
new file mode 100644 (file)
index 0000000..b721237
--- /dev/null
@@ -0,0 +1,55 @@
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::CheckBreaks 0.019
+
+use Test::More tests => 4;
+
+SKIP: {
+    eval { +require Module::Runtime::Conflicts; Module::Runtime::Conflicts->check_conflicts };
+    skip('no Module::Runtime::Conflicts module found', 1) if not $INC{'Module/Runtime/Conflicts.pm'};
+
+    diag $@ if $@;
+    pass 'conflicts checked via Module::Runtime::Conflicts';
+}
+
+SKIP: {
+    eval { +require Moose::Conflicts; Moose::Conflicts->check_conflicts };
+    skip('no Moose::Conflicts module found', 1) if not $INC{'Moose/Conflicts.pm'};
+
+    diag $@ if $@;
+    pass 'conflicts checked via Moose::Conflicts';
+}
+
+SKIP: {
+    eval { +require Package::Stash::Conflicts; Package::Stash::Conflicts->check_conflicts };
+    skip('no Package::Stash::Conflicts module found', 1) if not $INC{'Package/Stash/Conflicts.pm'};
+
+    diag $@ if $@;
+    pass 'conflicts checked via Package::Stash::Conflicts';
+}
+
+# this data duplicates x_breaks in META.json
+my $breaks = {
+  "Class::MOP" => "<= 1.08",
+  "MooseX::Method::Signatures" => "<= 0.36",
+  "MooseX::Role::WithOverloading" => "<= 0.08",
+  "namespace::clean" => "<= 0.18"
+};
+
+use CPAN::Meta::Requirements;
+use CPAN::Meta::Check 0.011;
+
+my $reqs = CPAN::Meta::Requirements->new;
+$reqs->add_string_requirement($_, $breaks->{$_}) foreach keys %$breaks;
+
+our $result = CPAN::Meta::Check::check_requirements($reqs, 'conflicts');
+
+if (my @breaks = grep { defined $result->{$_} } keys %$result)
+{
+    diag 'Breakages found with Package-Stash:';
+    diag "$result->{$_}" for sort @breaks;
+    diag "\n", 'You should now update these modules!';
+}
+
+pass 'checked x_breaks data';
diff --git a/xt/author/00-compile.t b/xt/author/00-compile.t
new file mode 100644 (file)
index 0000000..a94352e
--- /dev/null
@@ -0,0 +1,99 @@
+use 5.006;
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058
+
+use Test::More 0.94;
+
+plan tests => 5;
+
+my @module_files = (
+    'Package/Stash.pm',
+    'Package/Stash/Conflicts.pm',
+    'Package/Stash/PP.pm'
+);
+
+my @scripts = (
+    'bin/package-stash-conflicts'
+);
+
+# no fake home requested
+
+my @switches = (
+    -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<perlfaq8/How can I capture STDERR from an external command?>
+    my $stderr = IO::Handle->new;
+
+    diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} }
+            $^X, @switches, '-e', "require q[$lib]"))
+        if $ENV{PERL_COMPILE_TEST_DEBUG};
+
+    my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-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;
+    }
+}
+
+foreach my $file (@scripts)
+{ SKIP: {
+    open my $fh, '<', $file or warn("Unable to open $file: $!"), next;
+    my $line = <$fh>;
+
+    close $fh and skip("$file isn't perl", 1) unless $line =~ /^#!\s*(?:\S*perl\S*)((?:\s+-\w*)*)(?:\s*#.*)?$/;
+    @switches = (@switches, split(' ', $1)) if $1;
+
+    close $fh and skip("$file uses -T; not testable with PERL5LIB", 1)
+        if grep { $_ eq '-T' } @switches and $ENV{PERL5LIB};
+
+    my $stderr = IO::Handle->new;
+
+    diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} }
+            $^X, @switches, '-c', $file))
+        if $ENV{PERL_COMPILE_TEST_DEBUG};
+
+    my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-c', $file);
+    binmode $stderr, ':crlf' if $^O eq 'MSWin32';
+    my @_warnings = <$stderr>;
+    waitpid($pid, 0);
+    is($?, 0, "$file compiled ok");
+
+    shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/
+        and not eval { +require blib; blib->VERSION('1.01') };
+
+    # in older perls, -c output is simply the file portion of the path being tested
+    if (@_warnings = grep { !/\bsyntax OK$/ }
+        grep { chomp; $_ ne (File::Spec->splitpath($file))[2] } @_warnings)
+    {
+        warn @_warnings;
+        push @warnings, @_warnings;
+    }
+} }
+
+
+
+is(scalar(@warnings), 0, 'no warnings found')
+    or diag 'got warnings: ', explain(\@warnings);
+
+BAIL_OUT("Compilation problems") if !Test::More->builder->is_passing;
diff --git a/xt/author/distmeta.t b/xt/author/distmeta.t
new file mode 100644 (file)
index 0000000..c2280dc
--- /dev/null
@@ -0,0 +1,6 @@
+#!perl
+# This file was automatically generated by Dist::Zilla::Plugin::MetaTests.
+
+use Test::CPAN::Meta;
+
+meta_yaml_ok();
diff --git a/xt/author/eol.t b/xt/author/eol.t
new file mode 100644 (file)
index 0000000..8915d5a
--- /dev/null
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19
+
+use Test::More 0.88;
+use Test::EOL;
+
+my @files = (
+    'bin/package-stash-conflicts',
+    'lib/Package/Stash.pm',
+    'lib/Package/Stash/Conflicts.pm',
+    'lib/Package/Stash/PP.pm',
+    't/00-report-prereqs.dd',
+    't/00-report-prereqs.t',
+    't/addsub.t',
+    't/anon-basic.t',
+    't/anon.t',
+    't/bare-anon-basic.t',
+    't/bare-anon.t',
+    't/basic.t',
+    't/compile-time.t',
+    't/edge-cases.t',
+    't/extension.t',
+    't/get.t',
+    't/impl-selection/basic-pp.t',
+    't/impl-selection/basic-xs.t',
+    't/impl-selection/bug-rt-78272.t',
+    't/impl-selection/choice.t',
+    't/impl-selection/env.t',
+    't/impl-selection/var.t',
+    't/io.t',
+    't/isa.t',
+    't/lib/CompileTime.pm',
+    't/lib/Package/Stash.pm',
+    't/magic.t',
+    't/paamayim_nekdotayim.t',
+    't/scalar-values.t',
+    't/stash-deletion.t',
+    't/synopsis.t',
+    't/warnings-taint.t',
+    't/warnings.t',
+    't/zzz-check-breaks.t',
+    'xt/author/00-compile.t',
+    'xt/author/distmeta.t',
+    'xt/author/eol.t',
+    'xt/author/kwalitee.t',
+    'xt/author/leaks-debug.t',
+    'xt/author/leaks.t',
+    'xt/author/minimum-version.t',
+    'xt/author/mojibake.t',
+    'xt/author/no-tabs.t',
+    'xt/author/pod-no404s.t',
+    'xt/author/pod-spell.t',
+    'xt/author/pod-syntax.t',
+    'xt/author/portability.t',
+    'xt/release/changes_has_content.t',
+    'xt/release/cpan-changes.t'
+);
+
+eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
+done_testing;
diff --git a/xt/author/kwalitee.t b/xt/author/kwalitee.t
new file mode 100644 (file)
index 0000000..c986546
--- /dev/null
@@ -0,0 +1,9 @@
+# this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.12
+use strict;
+use warnings;
+use Test::More 0.88;
+use Test::Kwalitee 1.21 'kwalitee_ok';
+
+kwalitee_ok();
+
+done_testing;
diff --git a/xt/author/leaks-debug.t b/xt/author/leaks-debug.t
new file mode 100644 (file)
index 0000000..36fa041
--- /dev/null
@@ -0,0 +1,229 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+use Test::LeakTrace;
+
+BEGIN { $^P |= 0x210 } # PERLDBf_SUBLINE
+
+use Package::Stash;
+use Symbol;
+
+{
+    package Bar;
+}
+
+{
+    package Baz;
+    our $foo;
+    sub bar { }
+    use constant baz => 1;
+    our %quux = (a => 'b');
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Foo');
+    } "object construction doesn't leak";
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Bar');
+    } "object construction doesn't leak, with an existing package";
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Baz');
+    } "object construction doesn't leak, with an existing package with things in it";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->name;
+    } "name accessor doesn't leak";
+    no_leaks_ok {
+        $foo->namespace;
+    } "namespace accessor doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->add_symbol('$scalar');
+    } "add_symbol scalar with no initializer doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('@array');
+    } "add_symbol array with no initializer doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('%hash');
+    } "add_symbol hash with no initializer doesn't leak";
+    { local $TODO = "not sure why this leaks";
+    no_leaks_ok {
+        $foo->add_symbol('io');
+    } "add_symbol io with no initializer doesn't leak";
+    }
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->add_symbol('$scalar_init' => 1);
+    } "add_symbol scalar doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('@array_init' => []);
+    } "add_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('%hash_init' => {});
+    } "add_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('&code_init' => sub { "foo" });
+    } "add_symbol code doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('io_init' => Symbol::geniosym);
+    } "add_symbol io doesn't leak";
+    is(exception {
+        is(Foo->code_init, 'foo', "sub installed correctly")
+    }, undef, "code_init exists");
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->remove_symbol('$scalar_init');
+    } "remove_symbol scalar doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('@array_init');
+    } "remove_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('%hash_init');
+    } "remove_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('&code_init');
+    } "remove_symbol code doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('io_init');
+    } "remove_symbol io doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    $foo->add_symbol("${_}glob") for ('$', '@', '%', '');
+    no_leaks_ok {
+        $foo->remove_glob('glob');
+    } "remove_glob doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->has_symbol('io');
+    } "has_symbol io doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('%hash');
+    } "has_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('@array_init');
+    } "has_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('$glob');
+    } "has_symbol nonexistent scalar doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('&something_else');
+    } "has_symbol nonexistent code doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->get_symbol('io');
+    } "get_symbol io doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('%hash');
+    } "get_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('@array_init');
+    } "get_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('$glob');
+    } "get_symbol nonexistent scalar doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('&something_else');
+    } "get_symbol nonexistent code doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    ok(!$foo->has_symbol('$glob'));
+    ok(!$foo->has_symbol('@array_init'));
+    no_leaks_ok {
+        $foo->get_or_add_symbol('io');
+        $foo->get_or_add_symbol('%hash');
+        my @super = ('Exporter');
+        @{$foo->get_or_add_symbol('@ISA')} = @super;
+        $foo->get_or_add_symbol('$glob');
+    } "get_or_add_symbol doesn't leak";
+    { local $TODO = $] < 5.010
+        ? "undef scalars aren't visible on 5.8"
+        : undef;
+    ok($foo->has_symbol('$glob'));
+    }
+    is(ref($foo->get_symbol('$glob')), 'SCALAR');
+    ok($foo->has_symbol('@ISA'));
+    is(ref($foo->get_symbol('@ISA')), 'ARRAY');
+    is_deeply($foo->get_symbol('@ISA'), ['Exporter']);
+    isa_ok('Foo', 'Exporter');
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    my $baz = Package::Stash->new('Baz');
+    no_leaks_ok {
+        $foo->list_all_symbols;
+        $foo->list_all_symbols('SCALAR');
+        $foo->list_all_symbols('CODE');
+        $baz->list_all_symbols('CODE');
+    } "list_all_symbols doesn't leak";
+}
+
+{
+    package Blah;
+    use constant 'baz';
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    my $blah = Package::Stash->new('Blah');
+    no_leaks_ok {
+        $foo->get_all_symbols;
+        $foo->get_all_symbols('SCALAR');
+        $foo->get_all_symbols('CODE');
+        $blah->get_all_symbols('CODE');
+    } "get_all_symbols doesn't leak";
+}
+
+# mimic CMOP::create_anon_class
+{
+    local $TODO = $] < 5.010 ? "deleting stashes is inherently leaky on 5.8"
+                             : undef;
+    my $i = 0;
+    no_leaks_ok {
+        $i++;
+        eval "package Quux$i; 1;";
+        my $quux = Package::Stash->new("Quux$i");
+        $quux->get_or_add_symbol('@ISA');
+        delete $::{'Quux' . $i . '::'};
+    } "get_symbol doesn't leak during glob expansion";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        eval { $foo->add_symbol('&blorg') };
+    } "doesn't leak on errors";
+}
+
+done_testing;
diff --git a/xt/author/leaks.t b/xt/author/leaks.t
new file mode 100644 (file)
index 0000000..2457c0d
--- /dev/null
@@ -0,0 +1,227 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Test::More;
+use Test::Fatal;
+use Test::LeakTrace;
+
+use Package::Stash;
+use Symbol;
+
+{
+    package Bar;
+}
+
+{
+    package Baz;
+    our $foo;
+    sub bar { }
+    use constant baz => 1;
+    our %quux = (a => 'b');
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Foo');
+    } "object construction doesn't leak";
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Bar');
+    } "object construction doesn't leak, with an existing package";
+}
+
+{
+    no_leaks_ok {
+        Package::Stash->new('Baz');
+    } "object construction doesn't leak, with an existing package with things in it";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->name;
+    } "name accessor doesn't leak";
+    no_leaks_ok {
+        $foo->namespace;
+    } "namespace accessor doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->add_symbol('$scalar');
+    } "add_symbol scalar with no initializer doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('@array');
+    } "add_symbol array with no initializer doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('%hash');
+    } "add_symbol hash with no initializer doesn't leak";
+    { local $TODO = "not sure why this leaks";
+    no_leaks_ok {
+        $foo->add_symbol('io');
+    } "add_symbol io with no initializer doesn't leak";
+    }
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->add_symbol('$scalar_init' => 1);
+    } "add_symbol scalar doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('@array_init' => []);
+    } "add_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('%hash_init' => {});
+    } "add_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('&code_init' => sub { "foo" });
+    } "add_symbol code doesn't leak";
+    no_leaks_ok {
+        $foo->add_symbol('io_init' => Symbol::geniosym);
+    } "add_symbol io doesn't leak";
+    is(exception {
+        is(Foo->code_init, 'foo', "sub installed correctly")
+    }, undef, "code_init exists");
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->remove_symbol('$scalar_init');
+    } "remove_symbol scalar doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('@array_init');
+    } "remove_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('%hash_init');
+    } "remove_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('&code_init');
+    } "remove_symbol code doesn't leak";
+    no_leaks_ok {
+        $foo->remove_symbol('io_init');
+    } "remove_symbol io doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    $foo->add_symbol("${_}glob") for ('$', '@', '%', '');
+    no_leaks_ok {
+        $foo->remove_glob('glob');
+    } "remove_glob doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->has_symbol('io');
+    } "has_symbol io doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('%hash');
+    } "has_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('@array_init');
+    } "has_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('$glob');
+    } "has_symbol nonexistent scalar doesn't leak";
+    no_leaks_ok {
+        $foo->has_symbol('&something_else');
+    } "has_symbol nonexistent code doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        $foo->get_symbol('io');
+    } "get_symbol io doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('%hash');
+    } "get_symbol hash doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('@array_init');
+    } "get_symbol array doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('$glob');
+    } "get_symbol nonexistent scalar doesn't leak";
+    no_leaks_ok {
+        $foo->get_symbol('&something_else');
+    } "get_symbol nonexistent code doesn't leak";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    ok(!$foo->has_symbol('$glob'));
+    ok(!$foo->has_symbol('@array_init'));
+    no_leaks_ok {
+        $foo->get_or_add_symbol('io');
+        $foo->get_or_add_symbol('%hash');
+        my @super = ('Exporter');
+        @{$foo->get_or_add_symbol('@ISA')} = @super;
+        $foo->get_or_add_symbol('$glob');
+    } "get_or_add_symbol doesn't leak";
+    { local $TODO = $] < 5.010
+        ? "undef scalars aren't visible on 5.8"
+        : undef;
+    ok($foo->has_symbol('$glob'));
+    }
+    is(ref($foo->get_symbol('$glob')), 'SCALAR');
+    ok($foo->has_symbol('@ISA'));
+    is(ref($foo->get_symbol('@ISA')), 'ARRAY');
+    is_deeply($foo->get_symbol('@ISA'), ['Exporter']);
+    isa_ok('Foo', 'Exporter');
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    my $baz = Package::Stash->new('Baz');
+    no_leaks_ok {
+        $foo->list_all_symbols;
+        $foo->list_all_symbols('SCALAR');
+        $foo->list_all_symbols('CODE');
+        $baz->list_all_symbols('CODE');
+    } "list_all_symbols doesn't leak";
+}
+
+{
+    package Blah;
+    use constant 'baz';
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    my $blah = Package::Stash->new('Blah');
+    no_leaks_ok {
+        $foo->get_all_symbols;
+        $foo->get_all_symbols('SCALAR');
+        $foo->get_all_symbols('CODE');
+        $blah->get_all_symbols('CODE');
+    } "get_all_symbols doesn't leak";
+}
+
+# mimic CMOP::create_anon_class
+{
+    local $TODO = $] < 5.010 ? "deleting stashes is inherently leaky on 5.8"
+                             : undef;
+    my $i = 0;
+    no_leaks_ok {
+        $i++;
+        eval "package Quux$i; 1;";
+        my $quux = Package::Stash->new("Quux$i");
+        $quux->get_or_add_symbol('@ISA');
+        delete $::{'Quux' . $i . '::'};
+    } "get_symbol doesn't leak during glob expansion";
+}
+
+{
+    my $foo = Package::Stash->new('Foo');
+    no_leaks_ok {
+        eval { $foo->add_symbol('&blorg') };
+    } "doesn't leak on errors";
+}
+
+done_testing;
diff --git a/xt/author/minimum-version.t b/xt/author/minimum-version.t
new file mode 100644 (file)
index 0000000..6551d45
--- /dev/null
@@ -0,0 +1,6 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::MinimumVersion;
+all_minimum_version_ok( qq{5.008001} );
diff --git a/xt/author/mojibake.t b/xt/author/mojibake.t
new file mode 100644 (file)
index 0000000..5ef161e
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl
+
+use strict;
+use warnings qw(all);
+
+use Test::More;
+use Test::Mojibake;
+
+all_files_encoding_ok();
diff --git a/xt/author/no-tabs.t b/xt/author/no-tabs.t
new file mode 100644 (file)
index 0000000..c712be7
--- /dev/null
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15
+
+use Test::More 0.88;
+use Test::NoTabs;
+
+my @files = (
+    'bin/package-stash-conflicts',
+    'lib/Package/Stash.pm',
+    'lib/Package/Stash/Conflicts.pm',
+    'lib/Package/Stash/PP.pm',
+    't/00-report-prereqs.dd',
+    't/00-report-prereqs.t',
+    't/addsub.t',
+    't/anon-basic.t',
+    't/anon.t',
+    't/bare-anon-basic.t',
+    't/bare-anon.t',
+    't/basic.t',
+    't/compile-time.t',
+    't/edge-cases.t',
+    't/extension.t',
+    't/get.t',
+    't/impl-selection/basic-pp.t',
+    't/impl-selection/basic-xs.t',
+    't/impl-selection/bug-rt-78272.t',
+    't/impl-selection/choice.t',
+    't/impl-selection/env.t',
+    't/impl-selection/var.t',
+    't/io.t',
+    't/isa.t',
+    't/lib/CompileTime.pm',
+    't/lib/Package/Stash.pm',
+    't/magic.t',
+    't/paamayim_nekdotayim.t',
+    't/scalar-values.t',
+    't/stash-deletion.t',
+    't/synopsis.t',
+    't/warnings-taint.t',
+    't/warnings.t',
+    't/zzz-check-breaks.t',
+    'xt/author/00-compile.t',
+    'xt/author/distmeta.t',
+    'xt/author/eol.t',
+    'xt/author/kwalitee.t',
+    'xt/author/leaks-debug.t',
+    'xt/author/leaks.t',
+    'xt/author/minimum-version.t',
+    'xt/author/mojibake.t',
+    'xt/author/no-tabs.t',
+    'xt/author/pod-no404s.t',
+    'xt/author/pod-spell.t',
+    'xt/author/pod-syntax.t',
+    'xt/author/portability.t',
+    'xt/release/changes_has_content.t',
+    'xt/release/cpan-changes.t'
+);
+
+notabs_ok($_) foreach @files;
+done_testing;
diff --git a/xt/author/pod-no404s.t b/xt/author/pod-no404s.t
new file mode 100644 (file)
index 0000000..eb9760c
--- /dev/null
@@ -0,0 +1,21 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More;
+
+foreach my $env_skip ( qw(
+  SKIP_POD_NO404S
+  AUTOMATED_TESTING
+) ){
+  plan skip_all => "\$ENV{$env_skip} is set, skipping"
+    if $ENV{$env_skip};
+}
+
+eval "use Test::Pod::No404s";
+if ( $@ ) {
+  plan skip_all => 'Test::Pod::No404s required for testing POD';
+}
+else {
+  all_pod_files_ok();
+}
diff --git a/xt/author/pod-spell.t b/xt/author/pod-spell.t
new file mode 100644 (file)
index 0000000..3ac730d
--- /dev/null
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+use Test::More;
+
+# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007005
+use Test::Spelling 0.12;
+use Pod::Wordlist;
+
+
+add_stopwords(<DATA>);
+all_pod_files_spelling_ok( qw( examples lib script t xt ) );
+__DATA__
+Bunce
+Carlos
+Christian
+Conflicts
+Dave
+Etheridge
+Fredric
+Hunter
+Jesse
+Justin
+Karen
+Kent
+Lima
+Little
+Luehrs
+Niko
+PP
+Package
+Renee
+Rolsky
+Stash
+Stevan
+Tim
+Tyni
+Walde
+autarch
+bin
+carlos
+doy
+ether
+irc
+justin
+kentfredric
+lib
+ntyni
+package
+reb
+stevan
+walde
diff --git a/xt/author/pod-syntax.t b/xt/author/pod-syntax.t
new file mode 100644 (file)
index 0000000..e563e5d
--- /dev/null
@@ -0,0 +1,7 @@
+#!perl
+# 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();
diff --git a/xt/author/portability.t b/xt/author/portability.t
new file mode 100644 (file)
index 0000000..c531252
--- /dev/null
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+eval 'use Test::Portability::Files';
+plan skip_all => 'Test::Portability::Files required for testing portability'
+    if $@;
+
+run_tests();
diff --git a/xt/release/changes_has_content.t b/xt/release/changes_has_content.t
new file mode 100644 (file)
index 0000000..fa313f6
--- /dev/null
@@ -0,0 +1,42 @@
+use Test::More tests => 2;
+
+note 'Checking Changes';
+my $changes_file = 'Changes';
+my $newver = '0.40';
+my $trial_token = '-TRIAL';
+my $encoding = 'UTF-8';
+
+SKIP: {
+    ok(-e $changes_file, "$changes_file file exists")
+        or skip 'Changes is missing', 1;
+
+    ok(_get_changes($newver), "$changes_file has content for $newver");
+}
+
+done_testing;
+
+sub _get_changes
+{
+    my $newver = shift;
+
+    # parse changelog to find commit message
+    open(my $fh, '<', $changes_file) or die "cannot open $changes_file: $!";
+    my $changelog = join('', <$fh>);
+    if ($encoding) {
+        require Encode;
+        $changelog = Encode::decode($encoding, $changelog, Encode::FB_CROAK());
+    }
+    close $fh;
+
+    my @content =
+        grep { /^$newver(?:$trial_token)?(?:\s+|$)/ ... /^\S/ } # from newver to un-indented
+        split /\n/, $changelog;
+    shift @content; # drop the version line
+
+    # drop unindented last line and trailing blank lines
+    pop @content while ( @content && $content[-1] =~ /^(?:\S|\s*$)/ );
+
+    # return number of non-blank lines
+    return scalar @content;
+}
+
diff --git a/xt/release/cpan-changes.t b/xt/release/cpan-changes.t
new file mode 100644 (file)
index 0000000..286005a
--- /dev/null
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::CPAN::Changes 0.012
+
+use Test::More 0.96 tests => 1;
+use Test::CPAN::Changes;
+subtest 'changes_ok' => sub {
+    changes_file_ok('Changes');
+};