Tie-ShadowHash-2.01000755001750001750 014320317257 13525 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/.gitignore000444001750001750 23614320317257 15633 0ustar00eagleeagle000000000000/Build /Build.bat /MANIFEST.bak /MYMETA.json /MYMETA.json.lock /MYMETA.yml /Tie-ShadowHash-*/ /Tie-ShadowHash-*.tar.gz /_build/ /blib/ /cover_db/ /pm_to_blib Tie-ShadowHash-2.01/Build.PL000444001750001750 241514320317257 15160 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Build script for Tie::ShadowHash # # Copyright 1999, 2010, 2022 Russ Allbery # # SPDX-License-Identifier: GPL-1.0-or-later OR Artistic-1.0-Perl use 5.024; use autodie; use warnings; use Module::Build; # Basic package configuration. #<<< my $build = Module::Build->new( module_name => 'Tie::ShadowHash', dist_author => 'Russ Allbery ', license => 'perl', recursive_test_files => 1, add_to_cleanup => [qw(MANIFEST.bak MYMETA.json.lock cover_db)], # Add additional package metadata. meta_merge => { 'meta-spec' => { version => '2' }, resources => { bugtracker => { web => 'https://github.com/rra/shadowhash/issues', }, homepage => 'https://www.eyrie.org/~eagle/software/shadowhash', repository => { url => 'https://github.com/rra/shadowhash.git', web => 'https://github.com/rra/shadowhash', type => 'git', }, }, }, # Other package relationships. configure_requires => { 'Module::Build' => 0.28 }, requires => { perl => '5.024' }, ); #>>> # Generate the build script. $build->create_build_script; Tie-ShadowHash-2.01/Changes000444001750001750 170514320317257 15160 0ustar00eagleeagle000000000000 Revision history for Tie::ShadowHash 2.01 - 2022-10-08 - Fix test suite issues on Windows and when DB_File is not available. 2.00 - 2022-06-27 - Remove the new() method, since tie should always be used instead. - Convert the build system to Module::Build. - Document the behavior of SCALAR. 1.00 - 2010-04-03 - Correctly return undef from FETCH if undef was explicitly stored. - Document the add() method. - Do not return shadowed keys from NEXTKEY iteration. - Depend on Perl 5.006 or later and use its syntax. - Change the dist name to Tie-ShadowHash from ShadowHash. 0.07 - 2002-07-27 - Add SEE ALSO and COPYRIGHT AND LICENSE documentation sections. 0.06 - 1999-06-02 - Document tagged data sources, particularly the "text" data souce. 0.05 - 1999-04-03 - Fix skipping of deleted entries in NEXTKEY. - Use AnyDBM_File for the test suite since SDBM isn't built on Mac OS. 0.03 - 1999-03-22 - Initial public release. Tie-ShadowHash-2.01/LICENSE000444001750001750 524514320317257 14675 0ustar00eagleeagle000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Comment: This file documents the copyright statements and licenses for every file in this package in a machine-readable format. For a less detailed, higher-level overview, see README. . For any copyright year range specified as YYYY-ZZZZ in this file, the range specifies every single year in that closed interval. Files: * Copyright: 1999, 2002, 2010, 2022 Russ Allbery License: Perl This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. This means that you may choose between the two licenses that Perl is released under: the GNU GPL and the Artistic License. Please see your Perl distribution for the details and copies of the licenses. Files: MANIFEST.SKIP t/data/perltidyrc Copyright: 2012-2013 The Board of Trustees of the Leland Stanford Junior University 2018, 2021-2022 Russ Allbery License: all-permissive Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. Files: t/data/perlcriticrc t/docs/changes.t t/docs/pod-coverage.t t/docs/pod-spelling.t t/docs/pod.t t/docs/spdx-license.t t/docs/synopsis.t t/lib/Test/RRA.pm t/lib/Test/RRA/Config.pm t/style/critic.t t/style/kwalitee.t t/style/minimum-version.t t/style/obsolete-strings.t t/style/strict.t Copyright: 2011-2014 The Board of Trustees of the Leland Stanford Junior University 2015-2016, 2018-2022 Russ Allbery License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Tie-ShadowHash-2.01/MANIFEST000444001750001750 117014320317257 15012 0ustar00eagleeagle000000000000.github/dependabot.yml .github/workflows/build.yaml .gitignore Build.PL Changes cpanfile docs/docknot.yaml lib/Tie/ShadowHash.pm LICENSE MANIFEST This list of files MANIFEST.SKIP README README.md t/api/basic.t t/data/first.txt t/data/full t/data/pairs.txt t/data/perl.conf t/data/perlcriticrc t/data/perltidyrc t/data/second.txt t/data/triples.txt t/docs/changes.t t/docs/pod-coverage.t t/docs/pod-spelling.t t/docs/pod.t t/docs/spdx-license.t t/docs/synopsis.t t/lib/Test/RRA.pm t/lib/Test/RRA/Config.pm t/style/critic.t t/style/kwalitee.t t/style/minimum-version.t t/style/obsolete-strings.t t/style/strict.t META.yml META.json Tie-ShadowHash-2.01/MANIFEST.SKIP000444001750001750 303014320317257 15554 0ustar00eagleeagle000000000000# -*- conf -*- # # Files to ignore when generating a Perl module manifest. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Copyright 2018, 2022 Russ Allbery # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice and # this notice are preserved. This file is offered as-is, without any # warranty. # # SPDX-License-Identifier: FSFAP # Avoid version control files. ^\.git/ # Avoid generated build files. \bblib/ # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ \bBuild.bat$ \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# \.bak$ \.tmp$ \.# \.rej$ # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store # Mac OSX SMB mount metadata files \B\._ # Avoid Devel::Cover and Devel::CoverX::Covered files. \bcover_db\b \bcovered\b # Avoid archives of this distribution \b[\w-]+-[\d\.\_]+\.tar\..z$ \b[\w-]+-[\d\.\_]+\.zip$ # MYMETA.json and MYMETA.yml are intentionally not ignored because otherwise # Test::Kwalitee fails (https://rt.cpan.org/Ticket/Display.html?id=143514). # They will be added back in by Module::Build during ./Build manifest, and # have to (unfortunately) be removed by hand. # # This is not entirely correct, since they should indeed be ignored when # creating the distribution, but it happens to work for me. See the bug for # more details. Tie-ShadowHash-2.01/META.json000444001750001750 235414320317257 15307 0ustar00eagleeagle000000000000{ "abstract" : "Merge multiple data sources into a hash", "author" : [ "Russ Allbery " ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4231", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Tie-ShadowHash", "prereqs" : { "configure" : { "requires" : { "Module::Build" : "0.28" } }, "runtime" : { "requires" : { "perl" : "5.024" } } }, "provides" : { "Tie::ShadowHash" : { "file" : "lib/Tie/ShadowHash.pm", "version" : "2.01" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/rra/shadowhash/issues" }, "homepage" : "https://www.eyrie.org/~eagle/software/shadowhash", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "https://github.com/rra/shadowhash.git", "web" : "https://github.com/rra/shadowhash" } }, "version" : "2.01", "x_serialization_backend" : "JSON::PP version 4.06" } Tie-ShadowHash-2.01/META.yml000444001750001750 142714320317257 15137 0ustar00eagleeagle000000000000--- abstract: 'Merge multiple data sources into a hash' author: - 'Russ Allbery ' build_requires: {} configure_requires: Module::Build: '0.28' dynamic_config: 1 generated_by: 'Module::Build version 0.4231, 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: Tie-ShadowHash provides: Tie::ShadowHash: file: lib/Tie/ShadowHash.pm version: '2.01' requires: perl: '5.024' resources: bugtracker: https://github.com/rra/shadowhash/issues homepage: https://www.eyrie.org/~eagle/software/shadowhash license: http://dev.perl.org/licenses/ repository: https://github.com/rra/shadowhash.git version: '2.01' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Tie-ShadowHash-2.01/README000444001750001750 1362114320317257 14565 0ustar00eagleeagle000000000000 Tie::ShadowHash 2.01 (Merge multiple data sources into a hash with overrides) Maintained by Russ Allbery Copyright 1999, 2002, 2010, 2022 Russ Allbery . This software is distributed under the same terms as Perl itself. Please see the section LICENSE below for more information. BLURB Tie::ShadowHash is a Perl module that lets you stack together multiple hash-like data structures, including tied hashes such as DB_File databases or text files parsed into a hash, and then treat them like a merged hash. Lookups are handled in the order of the added sources. You can store additional values, change values, and delete values from the hash and those actions will be reflected in later operations, but the underlying objects are not changed. DESCRIPTION If you have several key/value data sources in the form of Perl hashes, tied hashes (of whatever type, including on-disk databases tied with DB_File, GDBM_File, or similar modules), or text files that you want to turn into hashes, and you want to be able to query all of those sources of data at once for a particular key without having to check each one of them individually, this module may be what you're looking for. If you want to use a hash-like data source, even just one, but make modifications to its data over the course of your program that override its contents while your program runs but which don't have any permanent effect on it, this module may be what you're looking for. Tie::ShadowHash lets you create a "shadow hash" that looks like a regular Perl hash to your program but, behind the scenes, queries a list of data sources. All the data sources must also behave like Perl hashes, but that's the only constraint. They can be regular Perl hashes or other tied hashes, including tied DB_File or GDBM_File hashes or the like to access on-disk databases. All data sources are treated as read-only; modifications to any data is stored in the shadow hash itself, and subsequent accesses reflect any modifications, but none of the data sources are changed. REQUIREMENTS The only requirement for this module is Perl 5.024 or later. BUILDING AND INSTALLATION Tie::ShadowHash uses Module::Build and can be installed using the same process as any other Module::Build module: perl Build.PL ./Build ./Build install You will have to run the last command as root unless you're installing into a local Perl module tree in your home directory. TESTING Tie::ShadowHash comes with a test suite, which you can run after building with: ./Build test If a test fails, you can run a single test with verbose output via: ./Build test --test_files The following additional Perl modules will be used by the test suite if present: * Devel::Cover * Perl::Critic::Freenode * Test::CPAN::Changes (part of CPAN-Changes) * Test::Kwalitee * Test::MinimumVersion * Test::Perl::Critic * Test::Pod * Test::Pod::Coverage * Test::Spelling * Test::Strict * Test::Synopsis To enable tests that don't detect functionality problems but are used to sanity-check the release, set the environment variable RELEASE_TESTING to a true value. To enable tests that may be sensitive to the local environment or that produce a lot of false positives without uncovering many problems, set the environment variable AUTHOR_TESTING to a true value. THANKS To Chris Nandor for testing this module on the Mac, pointing out that SDBM_File wasn't available there, mentioning that SDBM was byte-order-dependent anyway, and suggesting using AnyDBM_File instead. SUPPORT The Tie::ShadowHash web page at: https://www.eyrie.org/~eagle/software/shadowhash/ will always have the current version of this package, the current documentation, and pointers to any additional resources. For bug tracking, use the issue tracker on GitHub: https://github.com/rra/shadowhash/issues However, please be aware that I tend to be extremely busy and work projects often take priority. I'll save your report and get to it as soon as I can, but it may take me a couple of months. SOURCE REPOSITORY Tie::ShadowHash is maintained using Git. You can access the current source on GitHub at: https://github.com/rra/shadowhash or by cloning the repository at: https://git.eyrie.org/git/perl/shadowhash.git or view the repository via the web at: https://git.eyrie.org/?p=perl/shadowhash.git The eyrie.org repository is the canonical one, maintained by the author, but using GitHub is probably more convenient for most purposes. Pull requests are gratefully reviewed and normally accepted. LICENSE The Tie::ShadowHash package as a whole is covered by the following copyright statement and license: Copyright 1999, 2002, 2010, 2022 Russ Allbery This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. This means that you may choose between the two licenses that Perl is released under: the GNU GPL and the Artistic License. Please see your Perl distribution for the details and copies of the licenses. Some files in this distribution are individually released under different licenses, all of which are compatible with the above general package license but which may require preservation of additional notices. All required notices, and detailed information about the licensing of each file, are recorded in the LICENSE file. Files covered by a license with an assigned SPDX License Identifier include SPDX-License-Identifier tags to enable automated processing of license information. See https://spdx.org/licenses/ for more information. For any copyright range specified by files in this package as YYYY-ZZZZ, the range specifies every single year in that closed interval. Tie-ShadowHash-2.01/README.md000444001750001750 1410114320317257 15156 0ustar00eagleeagle000000000000# Tie::ShadowHash [![Build status](https://github.com/rra/shadowhash/workflows/build/badge.svg)](https://github.com/rra/shadowhash/actions) [![CPAN version](https://img.shields.io/cpan/v/Tie-ShadowHash)](https://metacpan.org/release/Tie-ShadowHash) [![License](https://img.shields.io/cpan/l/Tie-ShadowHash)](https://github.com/rra/shadowhash/blob/master/LICENSE) [![Debian package](https://img.shields.io/debian/v/libtie-shadowhash-perl/unstable)](https://tracker.debian.org/pkg/libtie-shadowhash-perl) Copyright 1999, 2002, 2010, 2022 Russ Allbery . This software is distributed under the same terms as Perl itself. Please see the section [License](#license) below for more information. ## Blurb Tie::ShadowHash is a Perl module that lets you stack together multiple hash-like data structures, including tied hashes such as DB_File databases or text files parsed into a hash, and then treat them like a merged hash. Lookups are handled in the order of the added sources. You can store additional values, change values, and delete values from the hash and those actions will be reflected in later operations, but the underlying objects are not changed. ## Description If you have several key/value data sources in the form of Perl hashes, tied hashes (of whatever type, including on-disk databases tied with DB_File, GDBM_File, or similar modules), or text files that you want to turn into hashes, and you want to be able to query all of those sources of data at once for a particular key without having to check each one of them individually, this module may be what you're looking for. If you want to use a hash-like data source, even just one, but make modifications to its data over the course of your program that override its contents while your program runs but which don't have any permanent effect on it, this module may be what you're looking for. Tie::ShadowHash lets you create a "shadow hash" that looks like a regular Perl hash to your program but, behind the scenes, queries a list of data sources. All the data sources must also behave like Perl hashes, but that's the only constraint. They can be regular Perl hashes or other tied hashes, including tied DB_File or GDBM_File hashes or the like to access on-disk databases. All data sources are treated as read-only; modifications to any data is stored in the shadow hash itself, and subsequent accesses reflect any modifications, but none of the data sources are changed. ## Requirements The only requirement for this module is Perl 5.024 or later. ## Building and Installation Tie::ShadowHash uses Module::Build and can be installed using the same process as any other Module::Build module: ``` perl Build.PL ./Build ./Build install ``` You will have to run the last command as root unless you're installing into a local Perl module tree in your home directory. ## Testing Tie::ShadowHash comes with a test suite, which you can run after building with: ``` ./Build test ``` If a test fails, you can run a single test with verbose output via: ``` ./Build test --test_files ``` The following additional Perl modules will be used by the test suite if present: * Devel::Cover * Perl::Critic::Freenode * Test::CPAN::Changes (part of CPAN-Changes) * Test::Kwalitee * Test::MinimumVersion * Test::Perl::Critic * Test::Pod * Test::Pod::Coverage * Test::Spelling * Test::Strict * Test::Synopsis To enable tests that don't detect functionality problems but are used to sanity-check the release, set the environment variable `RELEASE_TESTING` to a true value. To enable tests that may be sensitive to the local environment or that produce a lot of false positives without uncovering many problems, set the environment variable `AUTHOR_TESTING` to a true value. ## Thanks To Chris Nandor for testing this module on the Mac, pointing out that SDBM_File wasn't available there, mentioning that SDBM was byte-order-dependent anyway, and suggesting using AnyDBM_File instead. ## Support The [Tie::ShadowHash web page](https://www.eyrie.org/~eagle/software/shadowhash/) will always have the current version of this package, the current documentation, and pointers to any additional resources. For bug tracking, use the [issue tracker on GitHub](https://github.com/rra/shadowhash/issues). However, please be aware that I tend to be extremely busy and work projects often take priority. I'll save your report and get to it as soon as I can, but it may take me a couple of months. ## Source Repository Tie::ShadowHash is maintained using Git. You can access the current source on [GitHub](https://github.com/rra/shadowhash) or by cloning the repository at: https://git.eyrie.org/git/perl/shadowhash.git or [view the repository on the web](https://git.eyrie.org/?p=perl/shadowhash.git). The eyrie.org repository is the canonical one, maintained by the author, but using GitHub is probably more convenient for most purposes. Pull requests are gratefully reviewed and normally accepted. ## License The Tie::ShadowHash package as a whole is covered by the following copyright statement and license: > Copyright 1999, 2002, 2010, 2022 > Russ Allbery > > This program is free software; you may redistribute it and/or modify it > under the same terms as Perl itself. This means that you may choose > between the two licenses that Perl is released under: the GNU GPL and the > Artistic License. Please see your Perl distribution for the details and > copies of the licenses. Some files in this distribution are individually released under different licenses, all of which are compatible with the above general package license but which may require preservation of additional notices. All required notices, and detailed information about the licensing of each file, are recorded in the LICENSE file. Files covered by a license with an assigned SPDX License Identifier include SPDX-License-Identifier tags to enable automated processing of license information. See https://spdx.org/licenses/ for more information. For any copyright range specified by files in this package as YYYY-ZZZZ, the range specifies every single year in that closed interval. Tie-ShadowHash-2.01/cpanfile000444001750001750 72014320317257 15345 0ustar00eagleeagle000000000000# -*- perl -*- on 'configure' => sub { requires 'Module::Build', '0.28'; }; on 'test' => sub { suggests 'Devel::Cover'; suggests 'Perl::Critic::Freenode'; suggests 'Test::CPAN::Changes'; suggests 'Test::Kwalitee'; suggests 'Test::MinimumVersion'; suggests 'Test::Perl::Critic'; suggests 'Test::Pod'; suggests 'Test::Pod::Coverage'; suggests 'Test::Spelling'; suggests 'Test::Strict'; suggests 'Test::Synopsis'; }; Tie-ShadowHash-2.01/.github000755001750001750 014320317257 15065 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/.github/dependabot.yml000444001750001750 16614320317257 20035 0ustar00eagleeagle000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" Tie-ShadowHash-2.01/.github/workflows000755001750001750 014320317257 17122 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/.github/workflows/build.yaml000444001750001750 141714320317257 21245 0ustar00eagleeagle000000000000name: build on: push: branches: - "*" tags: - "release/*" pull_request: branches: - main jobs: perl: env: AUTHOR_TESTING: 1 runs-on: ubuntu-latest strategy: fail-fast: false matrix: perl-version: - "5.36" - "5.34" - "5.32" - "5.30" - "5.28" - "5.26" - "5.24" container: image: perldocker/perl-tester:${{ matrix.perl-version }} steps: - uses: actions/checkout@v3 - uses: perl-actions/install-with-cpm@v1.4 with: cpanfile: "cpanfile" args: "--with-suggests" sudo: false - name: Build.PL run: perl Build.PL - name: Build test run: ./Build test Tie-ShadowHash-2.01/docs000755001750001750 014320317257 14455 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/docs/docknot.yaml000444001750001750 777714320317257 17161 0ustar00eagleeagle000000000000# Package metadata for Tie::ShadowHash. # # This file contains configuration for DocKnot used to generate # documentation files (like README.md) and web pages. Other documentation # in this package is generated automatically from these files as part of # the release process. For more information, see DocKnot's documentation. # # DocKnot is available from . # # Copyright 1999, 2002, 2010, 2022 Russ Allbery # # SPDX-License-Identifier: GPL-1.0-or-later OR Artistic-1.0-Perl format: v1 name: Tie::ShadowHash maintainer: Russ Allbery version: '2.01' synopsis: Merge multiple data sources into a hash with overrides license: name: Perl copyrights: - holder: Russ Allbery years: 1999, 2002, 2010, 2022 build: type: Module::Build distribution: cpan: Tie-ShadowHash packaging: debian: package: libtie-shadowhash-perl summary: | Tie::ShadowHash is packaged in Debian 6.0 (squeeze) and later as libtie-shadowhash-perl. Thanks to Stefan Hornburg (Racke) for the packaging. section: perl tarname: Tie-ShadowHash version: tie-shadowhash support: email: rra@cpan.org github: rra/shadowhash web: https://www.eyrie.org/~eagle/software/shadowhash/ vcs: browse: https://git.eyrie.org/?p=perl/shadowhash.git github: rra/shadowhash status: workflow: build type: Git url: https://git.eyrie.org/git/perl/shadowhash.git docs: user: - name: docs title: Module documentation blurb: | Tie::ShadowHash is a Perl module that lets you stack together multiple hash-like data structures, including tied hashes such as DB_File databases or text files parsed into a hash, and then treat them like a merged hash. Lookups are handled in the order of the added sources. You can store additional values, change values, and delete values from the hash and those actions will be reflected in later operations, but the underlying objects are not changed. description: | If you have several key/value data sources in the form of Perl hashes, tied hashes (of whatever type, including on-disk databases tied with DB_File, GDBM_File, or similar modules), or text files that you want to turn into hashes, and you want to be able to query all of those sources of data at once for a particular key without having to check each one of them individually, this module may be what you're looking for. If you want to use a hash-like data source, even just one, but make modifications to its data over the course of your program that override its contents while your program runs but which don't have any permanent effect on it, this module may be what you're looking for. Tie::ShadowHash lets you create a "shadow hash" that looks like a regular Perl hash to your program but, behind the scenes, queries a list of data sources. All the data sources must also behave like Perl hashes, but that's the only constraint. They can be regular Perl hashes or other tied hashes, including tied DB_File or GDBM_File hashes or the like to access on-disk databases. All data sources are treated as read-only; modifications to any data is stored in the shadow hash itself, and subsequent accesses reflect any modifications, but none of the data sources are changed. requirements: | The only requirement for this module is Perl 5.024 or later. test: lancaster: true suffix: | The following additional Perl modules will be used by the test suite if present: * Devel::Cover * Perl::Critic::Freenode * Test::CPAN::Changes (part of CPAN-Changes) * Test::Kwalitee * Test::MinimumVersion * Test::Perl::Critic * Test::Pod * Test::Pod::Coverage * Test::Spelling * Test::Strict * Test::Synopsis sections: - title: Thanks body: | To Chris Nandor for testing this module on the Mac, pointing out that SDBM_File wasn't available there, mentioning that SDBM was byte-order-dependent anyway, and suggesting using AnyDBM_File instead. Tie-ShadowHash-2.01/lib000755001750001750 014320317257 14273 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/lib/Tie000755001750001750 014320317257 15014 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/lib/Tie/ShadowHash.pm000444001750001750 3736314320317257 17574 0ustar00eagleeagle000000000000# Tie::ShadowHash -- Merge multiple data sources into a hash. # # This module combines multiple sources of data into a single tied hash, so # that they can all be queried simultaneously, the source of any given # key-value pair irrelevant to the client script. Data sources are searched # in the order that they're added to the shadow hash. Changes to the hashed # data aren't propagated back to the actual data files; instead, they're saved # within the tied hash and override any data obtained from the data sources. # # SPDX-License-Identifier: GPL-1.0-or-later OR Artistic-1.0-Perl ############################################################################## # Modules and declarations ############################################################################## package Tie::ShadowHash 2.01; use 5.024; use autodie; use warnings; use Carp qw(croak); ############################################################################## # Regular methods ############################################################################## # Given a file name and optionally a split regex, builds a hash out of the # contents of the file. # # If the split sub exists, use it to split each line into an array; if the # array has two elements, those are taken as the key and value. If there are # more, the value is an anonymous array containing everything but the first. # # If there's no split sub, take the entire line modulo the line terminator as # the key and the value the number of times it occurs in the file. # # $file - File containing the data # $split - Optional anonymous sub that splits a line into key and value # # Returns: Hash created by loading the file sub _text_source { my ($self, $file, $split) = @_; my %hash; open(my $fh, '<', $file); while (defined(my $line = <$fh>)) { chomp($line); if (defined($split)) { my ($key, @rest) = $split->($line); $hash{$key} = (@rest == 1) ? $rest[0] : [@rest]; } else { $hash{$line}++; } } close($fh); return \%hash; } # Add data sources to the shadow hash. # # Each data source is one of the following: # # - An anonymous array, in which case the first element is the type of source # and the rest are arguments. Currently, "text" is the only supported type. # # - A file name, which is taken to be a text file with each line as a key and # a value equal to the number of times that line appears. # # - A hash reference, possibly to a tied hash. # # @sources - Data sources to add # # Returns: True sub add { my ($self, @sources) = @_; for my $source (@sources) { if (ref($source) eq 'ARRAY') { my ($type, @args) = $source->@*; if ($type eq 'text') { $source = $self->_text_source(@args); } else { croak("invalid source type $type"); } } elsif (!ref($source)) { $source = $self->_text_source($source); } push($self->{SOURCES}->@*, $source); } return 1; } ############################################################################## # Tie methods ############################################################################## # Create a new tied hash. # # @sources - Sources to add to the new hash # # Returns: Newly created tied hash sub TIEHASH { my ($class, @sources) = @_; $class = ref($class) || $class; #<<< my $self = { DELETED => {}, # All keys that have been deleted EACH => -1, # Index of source currently being traversed OVERRIDE => {}, # Values set directly by the user SOURCES => [], # Array of all of the underlying hashes }; #>>> bless($self, $class); $self->add(@sources); return $self; } # Retrieve a value. # # This doesn't work quite right in the case of keys with undefined values, but # we can't make it work right since that would require using exists and a lot # of common data sources (such as NDBM_File tied hashes) don't implement # exists. # # $key - Key to look up # # Returns: Value for that key, undef if it is not present sub FETCH { my ($self, $key) = @_; if ($self->{DELETED}{$key}) { return; } elsif (exists($self->{OVERRIDE}{$key})) { return $self->{OVERRIDE}{$key}; } else { for my $source ($self->{SOURCES}->@*) { if (defined($source->{$key})) { return $source->{$key}; } } return; } } # Store a value. This goes into the override hash, which is checked before # any of the underlying data sources. # # $key - Key to store a value for # $value - Value to store sub STORE { my ($self, $key, $value) = @_; delete $self->{DELETED}{$key}; $self->{OVERRIDE}{$key} = $value; return; } # Delete a key. The key is flagged in the deleted hash, which ensures that # undef will be returned for any future retrieval. Dropping the override # value isn't required for currect future FETCH behavior, but it drops the # reference so that memory can be released. # # $key - Key to delete sub DELETE { my ($self, $key) = @_; delete $self->{OVERRIDE}{$key}; $self->{DELETED}{$key} = 1; return; } # Clear the hash. Removes all sources and all overrides and resets any # iteration. sub CLEAR { my ($self) = @_; $self->{DELETED} = {}; $self->{OVERRIDE} = {}; $self->{SOURCES} = []; $self->{EACH} = -1; return; } # Return whether a key exists. # # This could throw an exception if any underlying source doesn't support # exists (like NDBM_File). # # $key - Key to query for existence # # Returns: True if the key exists, false otherwise sub EXISTS { my ($self, $key) = @_; return if exists($self->{DELETED}{$key}); for my $source ($self->{OVERRIDE}, $self->{SOURCES}->@*) { return 1 if exists($source->{$key}); } return; } # Start an iteration. # # We have to reset the each counter on all hashes. For tied hashes, we call # FIRSTKEY directly because it's potentially more efficient than calling keys # on the hash. sub FIRSTKEY { my ($self) = @_; keys($self->{OVERRIDE}->%*); for my $source ($self->{SOURCES}->@*) { my $tie = tied($source); if ($tie) { $tie->FIRSTKEY(); } else { keys($source->%*); } } $self->{EACH} = -1; return $self->NEXTKEY(); } # Iterate through the hashes. # # Walk the sources by calling each on each one in turn, skipping deleted keys # and keys shadowed by earlier hashes and using $self->{EACH} to store the # number of source we're at. # # Returns: Next key in iteration, or undef if sources are exhausted sub NEXTKEY { my ($self) = @_; # EACH is the numeric index in the SOURCES list for the source we're # currently calling each on, or -1 if we're just starting and thus # operating on the OVERRIDE hash. # # We have to loop until we find the next value, which may take several # iterations since keys could have been overridden by an earlier hash or # deleted. SOURCE: while ($self->{EACH} < $self->{SOURCES}->@*) { my $key; ## no critic (Each) if ($self->{EACH} == -1) { $key = each($self->{OVERRIDE}->%*); } else { $key = each($self->{SOURCES}[$self->{EACH}]->%*); } ## use critic # If we got a valid result, we have to check against DELETED, # OVERRIDE, and all earlier sources before returning it. if (defined($key)) { if ($self->{DELETED}{$key}) { next; } elsif ($self->{EACH} >= 0 && exists($self->{OVERRIDE}{$key})) { next; } elsif ($self->{EACH} > 0) { for my $index (reverse(0 .. $self->{EACH} - 1)) { if (defined($self->{SOURCES}[$index]{$key})) { next SOURCE; } } } return $key; } $self->{EACH}++; } # We have exhausted all of the sources. return; } ############################################################################## # Module return value and documentation ############################################################################## # Make sure the module returns true. 1; __DATA__ =head1 NAME Tie::ShadowHash - Merge multiple data sources into a hash =for stopwords DBM Allbery =head1 SYNOPSIS use Tie::ShadowHash; use AnyDBM_File; use Fcntl qw(O_RDONLY); tie(my %db, 'AnyDBM_File', 'file', O_RDONLY, oct('666')); my $obj = tie(my %hash, 'Tie::ShadowHash', \%db, 'otherdata.txt'); # Accesses search %db first, then the hashed otherdata.txt. print "$hash{key}\n"; # Changes override data sources, but don't change them. $hash{key} = 'foo'; delete $hash{bar}; # Add more data sources on the fly. my %extra = (fee => 'fi', foe => 'fum'); $obj->add(\%extra); # Add a text file as a data source, taking the first "word" up # to whitespace on each line as the key and the rest of the line # as the value. my $split = sub { my ($line) = @_; split(q{ }, $line, 2) }; $obj->add([text => "pairs.txt", $split]); # Add a text file as a data source, splitting each line on # whitespace and taking the first "word" to be the key and an # anonymous array consisting of the remaining words to be the # data. $split = sub { my ($line) = @_; split(q{ }, $line) }; $obj->add([text => "triples.txt", $split]); =head1 DESCRIPTION This module merges together multiple sets of data in the form of hashes into a data structure that looks to Perl like a single simple hash. When that hash is accessed, the data structures managed by that shadow hash are searched in order they were added for that key. This allows the rest of a program simple and convenient access to a disparate set of data sources. The shadow hash can be modified, and the modifications override the data sources, but modifications aren't propagated back to the data sources. In other words, the shadow hash treats all data sources as read-only and saves your modifications in an overlay in memory. This lets you make changes to the shadow hash and have them reflected later in your program without affecting the underlying data in any way. This behavior is the reason why it is called a shadow hash. =head1 Constructing the hash Tie::ShadowHash takes one or more underlying data sources as additional arguments to tie(). Data sources can also be added later by calling the add() method on the object returned by tie(). A data source can be anything that looks like a hash. This includes other tied hashes, so you can include DB and DBM files as data sources for a shadow hash. If the data source is a scalar string instead of a hash reference, Tie::ShadowHash will treat that string as a file name and construct a hash from it. Each chomped line of the file will be a key, and the number of times that line is seen in the file will be the corresponding value. Tie::Shadowhash also supports special tagged data sources that can take options specifying their behavior. Tagged data sources are distinguished from normal data sources by passing them to tie() or add() as an array reference. The first element is the data source tag and the remaining elements are arguments for that data source. The following tagged data sources are supported: =over 4 =item C The arguments must be the file name of a text file and a reference to a sub. The sub is called for every line of the file, with that line as an argument, and is expected to return a list. The first element of the list will be the key, and the second and subsequent elements will be the value or values. If there is more than one value, the value stored in the hash and associated with that key is an anonymous array containing all of them. See the usage summary above for examples. =back =head1 Clearing the hash If the shadow hash is cleared by assigning the empty list to it, calling CLEAR(), or some other method, all data sources are dropped from the shadow hash. There is no other way of removing a data source from a shadow hash after it's been added (you can, of course, always untie the shadow hash and dispose of the underlying object if you saved it to destroy the shadow hash completely). =head1 INSTANCE METHODS =over 4 =item add(SOURCE [, SOURCE ...]) Adds the given sources to an existing shadow hash. This method can be called on the object returned by the initial tie() call. It takes the same arguments as the initial tie() and interprets them the same way. =back =head1 DIAGNOSTICS =over 4 =item invalid source type %s Tie::ShadowHash was given a tagged data source of an unknown type. The only currently supported tagged data source is C. =back If given a file name as a data source, Tie::ShadowHash will also raise an L exception if there is a problem with opening or reading that file. =head1 CAVEATS =head2 Iterating If you iterate through the keys of a shadow hash, it in turn will iterate through the keys of the underlying hash. Since Perl stores only one iterator position per hash, this means the shadow hash will reset any existing iterator positions in its underlying hashes. Iterating through both the shadow hash and one of its underlying hashes at the same time is undefined and will probably not do what you expect. =head2 untie If you are including tied hashes in a shadow hash, read L. Tie::ShadowHash stores a reference to those hashes. If you untie them out from under a shadow hash, you may not get the results you expect. If you put something in a shadow hash, you'll need to clean out the shadow hash as well as everything else that references a variable if you want to free it completely. =head2 EXISTS Not all tied hashes implement EXISTS; in particular, ODBM_File, NDBM_File, and some old versions of GDBM_File don't, and therefore AnyDBM_File doesn't either. Calling exists on a shadow hash that includes one of those tied hashes as a data source may therefore result in an exception. Tie::ShadowHash doesn't use exists except to implement the EXISTS method because of this. Because it can't use EXISTS due to the above problem, Tie::ShadowHash cannot correctly distinguish between a non-existent key and an existing key associated with an undefined value. This isn't a large problem, since many tied hashes can't store undefined values anyway, but it means that if one of your data sources contains a given key associated with an undefined value and one of your later data sources contains the same key but with a defined value, when the shadow hash is accessed using that key, it will return the first defined value it finds. This is an exception to the normal rule that all data sources are searched in order and the value returned by an access is the first value found. (Tie::ShadowHash does correctly handle undefined values stored directly in the shadow hash.) =head2 SCALAR Tie::ShadowHash does not implement SCALAR and therefore relies on the default Perl behavior, which is somewhat complex. See L for a partial description of this logic, which includes the note that Perl may incorrectly return true in a scalar context if the hash is cleared by repeatedly calling DELETE until it is empty. SCALAR on a shadow hash does not return a count of keys the way that it does for an untied hash. The value returned is either true or false and carries no other meaning. =head1 AUTHOR Russ Allbery =head1 COPYRIGHT AND LICENSE Copyright 1999, 2002, 2010, 2022 Russ Allbery This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L The current version of this module is always available from its web site at L. =cut # Local Variables: # copyright-at-end-flag: t # End: Tie-ShadowHash-2.01/t000755001750001750 014320317257 13770 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/api000755001750001750 014320317257 14541 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/api/basic.t000555001750001750 1423214320317257 16171 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Copyright 1999, 2002, 2010, 2022 Russ Allbery # # SPDX-License-Identifier: GPL-1.0-or-later OR Artistic-1.0-Perl use 5.024; use autodie; use warnings; use AnyDBM_File (); use Fcntl qw(O_CREAT O_RDONLY O_RDWR); use File::Spec (); use Test::More tests => 46; ## no critic (Miscellanea::ProhibitTies) require_ok('Tie::ShadowHash'); # Test setup. Tie an AnyDBM_File object and create a tied hash with something # interesting in it. my $data = File::Spec->catfile('t', 'data'); my $dbmfile = File::Spec->catfile($data, 'first'); my $db = tie(my %hash, 'AnyDBM_File', $dbmfile, O_RDWR | O_CREAT, oct('666')) or BAIL_OUT("Cannot create AnyDBM_File tied hash $dbmfile"); open(my $fh, '<', File::Spec->catfile($data, 'first.txt')); while (defined(my $line = <$fh>)) { chomp($line); $hash{$line} = 1; } close($fh); undef $db; untie(%hash); # Some basic checks against a text file. my $text = File::Spec->catfile($data, 'second.txt'); my $obj = tie(%hash, 'Tie::ShadowHash', $text); isa_ok($obj, 'Tie::ShadowHash'); is($hash{admin}, 1, 'Found existing key in text source'); ok(!exists($hash{meta}), 'Non-existing key returned false to exists'); $hash{meta} = 2; $hash{admin} = 2; is($hash{meta}, 2, 'Overriding non-existing key'); is($hash{admin}, 2, 'Overriding existing key'); is($hash{jp}, 1, 'Another untouched key is still correct'); delete $hash{jp}; ok(!exists($hash{jp}), '...and it does not exist after we delete it'); $hash{jp} = 2; is($hash{jp}, 2, '...and we can set it to another value'); # Tie only the dbm file and check some basic functionality. undef $obj; untie(%hash); tie(my %db, 'AnyDBM_File', $dbmfile, O_RDONLY, oct('666')) or BAIL_OUT("Cannot tie newly created db file $dbmfile"); $obj = tie(%hash, 'Tie::ShadowHash', \%db); isa_ok($obj, 'Tie::ShadowHash'); is($hash{meta}, 1, 'Found existing key in dbm source'); is($hash{admin}, undef, 'Non-existing key returns undef'); $hash{admin} = 2; is($hash{admin}, 2, 'Overriding existing key'); is($db{admin}, undef, '...and underlying source is unchanged'); delete $hash{meta}; is($hash{meta}, undef, 'Deleting existing key'); is($db{meta}, 1, '...and underlying source is unchanged'); # Check clearning the hash. %hash = (); is($hash{sg}, undef, 'Existing key is undefined after clearing'); # Add back in both the dbm file and the text file. is($obj->add(\%db, $text), 1, 'Adding sources'); is($hash{admin}, 1, 'Found data in text file'); is($hash{meta}, 1, 'Found data in dbm file'); is($hash{fooba}, undef, 'Keys missing in both fall through'); # Compare a keys listing with the full data. open($fh, '<', File::Spec->catfile($data, 'full')); my @full = sort <$fh>; close($fh); chomp(@full); is_deeply([sort keys(%hash)], \@full, 'Complete key listing matches'); # Make sure deleted keys are skipped in a key listing. delete $hash{sg}; my @keys = keys(%hash); is(scalar(@keys), scalar(@full) - 1, 'One fewer key after deletion'); ok(!(grep { $_ eq 'sg' } @keys), '...and the deleted key is missing'); # Add an additional hash with a key that duplicates a key from an earlier hash # and ensure that we don't see it twice in the keys listing. my %extra = (admin => 'foo'); is($obj->add(\%extra), 1, 'Adding another hash source succeeds'); @keys = keys(%hash); is(scalar(@keys), scalar(@full) - 1, 'Duplicate keys do not add to count'); is($hash{admin}, 1, '...and the earlier source still prevails'); # Restoring the deleted key should increment our key count again. $hash{sg} = 'override'; @keys = keys(%hash); is(scalar(@keys), scalar(@full), 'Setting a deleted key restores the count'); # Now add an override and ensure that doesn't cause duplicate keys either, but # adding a new key via an override should increase our key count. $hash{admin} = 'foo'; @keys = keys(%hash); is(scalar(@keys), scalar(@full), 'Overriden keys do not add to count'); is($hash{admin}, 'foo', '...and the override is effective'); $hash{override} = 1; @keys = keys(%hash); is(scalar(@keys), scalar(@full) + 1, 'Added keys do add to count'); # Clear the hash and then try adding a special text source with a sub to split # key and value. %hash = (); my $pairs = File::Spec->catfile($data, 'pairs.txt'); my $split = sub { my ($line) = @_; split(q{ }, $line, 2) }; is($obj->add([text => $pairs, $split]), 1, 'Adding special text source works'); my %full; open($fh, '<', $pairs); while (defined(my $line = <$fh>)) { chomp($line); my ($key, $value) = split(q{ }, $line, 2); $full{$key} = $value; } close($fh); is(scalar(keys(%full)), scalar(keys(%hash)), '...and has correct key count'); is_deeply(\%hash, \%full, '...and hashes compare equal'); # Add a special text source that returns an array of values. %hash = (); my $triples = File::Spec->catfile($data, 'triples.txt'); $split = sub { my ($line) = @_; split(q{ }, $line) }; is($obj->add([text => $triples, $split]), 1, 'Adding second source works'); undef %full; open($fh, '<', $triples); while (defined(my $line = <$fh>)) { chomp($line); my ($key, @value) = split(q{ }, $line); $full{$key} = [@value]; } close($fh); is(scalar(keys(%full)), scalar(keys(%hash)), '...and has correct key count'); for my $key (keys(%full)) { is_deeply($hash{$key}, $full{$key}, "...and value of $key is correct"); } # Test handling of the hash in a scalar context. %hash = (); ok(!scalar(%hash), 'Scalar value is false when the hash as been cleared'); %extra = (foo => 1, bar => 1); is($obj->add(\%extra), 1, 'Adding a hash works'); ok(scalar(%hash), '...and now the scalar value is true'); delete $hash{foo}; delete $hash{bar}; ok(!scalar(%hash), 'The scalar value is false after deleting both members'); # Ensure that storing an undefined value directly in the shadow hash works # properly with FETCH. %hash = (); is($obj->add(\%extra), 1, 'Adding the hash again works'); is($hash{foo}, 1, '...and the value of foo is what we expect'); $hash{foo} = undef; is($hash{foo}, undef, 'The value is undef after explicitly storing that'); # Clean up after ourselves (delete first* in $data except for first.txt). undef $obj; untie(%hash); untie(%db); opendir(my $dir, $data); for my $file (grep { m{ ^ first }xms } readdir($dir)) { if ($file ne 'first.txt') { unlink(File::Spec->catfile($data, $file)); } } closedir($dir); Tie-ShadowHash-2.01/t/data000755001750001750 014320317257 14701 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/data/first.txt000444001750001750 15314320317257 16705 0ustar00eagleeagle000000000000acraphobe acro aep ash dm eleck esp lnh meta misc mu ntb pulp raccies rc3 repost rev review rp sg sgn wchr Tie-ShadowHash-2.01/t/data/full000444001750001750 33514320317257 15704 0ustar00eagleeagle000000000000meta admin sg jp repost precog rev omega lnh hype rp starfall acraphobe racccafe rc3 www ash nop review kewl esp hapcc misc lbp eleck rant raccies grafek wchr ntc sgn osd ntb cleric pulp ip mu raccie dm fcc aep acra acro Tie-ShadowHash-2.01/t/data/pairs.txt000444001750001750 6214320317257 16653 0ustar00eagleeagle000000000000apple red or green orange orange banana yellow Tie-ShadowHash-2.01/t/data/perl.conf000444001750001750 26014320317257 16625 0ustar00eagleeagle000000000000# Configuration for Perl tests. -*- perl -*- # Default minimum version requirement for included Perl scripts. $MINIMUM_VERSION = '5.024'; # File must end with this line. 1; Tie-ShadowHash-2.01/t/data/perlcriticrc000444001750001750 1300714320317257 17467 0ustar00eagleeagle000000000000# -*- conf -*- # # Default configuration for perlcritic. Be sure to copy this into the source # for packages that run perlcritic tests automatically during the build for # reproducible test results. # # This file has been updated to match perlcritic 1.134. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2018-2022 Russ Allbery # Copyright 2011-2013 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT severity = 1 verbose = %f:%l:%c: [%p] %m (%e, Severity: %s)\n # I prefer this policy (a lot, actually), but other people in my group at # Stanford really didn't like it, so this is my compromise to agree with a # group coding style. [-CodeLayout::ProhibitParensWithBuiltins] # This conflicts with Subroutines::ProhibitExplicitReturnUndef and # Subroutines::RequireFinalReturn, and I prefer the brevity of the simple # return statement. I don't think the empty list versus undef behavior is # that confusing. # # This should be Community::EmptyReturn, which is the new name of the module, # but currently ignores have to use the Freenode::EmptyReturn name instead. [-Community::EmptyReturn] [-Freenode::EmptyReturn] # This recommends using given/when, but Perl has marked those as experimental # and cautions against using when. [-ControlStructures::ProhibitCascadingIfElse] # Stanford's coding style allows postfix unless for flow control. There # doesn't appear to be any way to allow it only for flow control (the logic # for "if" and "when" appears to be special-cased), so we have to allow unless # globally. [ControlStructures::ProhibitPostfixControls] allow = unless # This is handled with a separate test case that uses Test::Spelling. [-Documentation::PodSpelling] # The POD sections Perl::Critic wants are incompatible with the POD template # from perlpodstyle, which is what I use for my POD documentation. [-Documentation::RequirePodSections] # This problem was fixed in Perl 5.14, which now properly preserves the value # of $@ even if destructors run at exit from the eval block. [-ErrorHandling::RequireCheckingReturnValueOfEval] # The default of 9 is too small and forces weird code contortions. After some # experimentation, I've never found this helpful in driving useful refactors. [-InputOutput::RequireBriefOpen] # This is correct 80% of the time, but it isn't correct for a lot of scripts # inside packages, where maintaining $VERSION isn't worth the effort. # Unfortunately, there's no way to override it, so it gets turned off # globally. [-Modules::RequireVersionVar] # This sounds interesting but is actually useless. Any large blocks of # literal text, which does not add to the complexity of the regex, will set it # off. [-RegularExpressions::ProhibitComplexRegexes] # Produces false positives currently with postfix dereferencing (introduced in # Perl 5.20). See https://github.com/Perl-Critic/Perl-Critic/issues/578. [-References::ProhibitDoubleSigils] # Five arguments to a method has seemed reasonable at least once: a pair of # input file data and path, a pair of output file descriptor and path, and # a dict of additional arguments. [Subroutines::ProhibitManyArgs] skip_object = 1 # I generally don't want to require Readonly as a prerequisite for all my Perl # modules. [-ValuesAndExpressions::ProhibitConstantPragma] # A good idea, but there are too many places where this would be more # confusing than helpful. Pull out numbers if one might change them # independent of the algorithm, but don't do so for mathematical formulae. [-ValuesAndExpressions::ProhibitMagicNumbers] # This has never triggered on anything useful and keeps telling me to add # underscores to UNIX timestamps and port numbers, which is just silly. [-ValuesAndExpressions::RequireNumberSeparators] # IO::Uncompress::Gunzip puts the error message in a package variable. # Text::Wrap has a broken interface that requires use of package variables. # YAML::XS also cannot be configured without package variables. [Variables::ProhibitPackageVars] add_packages = IO::Uncompress::Gunzip Text::Wrap YAML::XS # use English was one of the worst ideas in the history of Perl. It makes the # code slightly more readable for amateurs at the cost of confusing # experienced Perl programmers and sending people in futile quests for where # these magical global variables are defined. [-Variables::ProhibitPunctuationVars] Tie-ShadowHash-2.01/t/data/perltidyrc000444001750001750 261214320317257 17143 0ustar00eagleeagle000000000000# -*- conf -*- # # Default options for perltidy for proper Perl code reformatting. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2021-2022 Russ Allbery # Copyright 2012-2013 # The Board of Trustees of the Leland Stanford Junior University # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice and # this notice are preserved. This file is offered as-is, without any # warranty. # # SPDX-License-Identifier: FSFAP -bbao # put line breaks before any operator -nbbc # don't force blank lines before comments (bad for else blocks) -boc # do not re-break lists, since perltidy is awful at this -ce # cuddle braces around else -l=79 # usually use 78, but don't want 79-long lines reformatted -nlop # disable vertical alignment of logical and ternary expressions -pt=2 # don't add extra whitespace around parentheses -sbt=2 # ...or square brackets -nsfs # no space before semicolon in for (not that I use this form) -nvc # disable vertical alignment of = and similar symbols -xci # improve indentation of nested structures Tie-ShadowHash-2.01/t/data/second.txt000444001750001750 16214320317257 17031 0ustar00eagleeagle000000000000admin jp precog omega hype starfall racccafe www nop kewl hapcc lbp rant grafek ntc osd cleric ip raccie fcc acra Tie-ShadowHash-2.01/t/data/triples.txt000444001750001750 17714320317257 17246 0ustar00eagleeagle000000000000comp news.announce.newgroups newsgroups-request@isc.org slac slac.groups news@news.stanford.edu us us.config usadmin@wwa.com Tie-ShadowHash-2.01/t/docs000755001750001750 014320317257 14720 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/docs/changes.t000555001750001750 323414320317257 16657 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Check Changes file for compliance with CPAN::Changes::Spec. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Copyright 2021 Russ Allbery # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_automated use_prereq); use Test::More; # Skip this test for normal user installs. skip_unless_automated('Changes format tests'); # Load prerequisite modules. use_prereq('Test::CPAN::Changes'); # Run the tests. changes_ok(); Tie-ShadowHash-2.01/t/docs/pod-coverage.t000555001750001750 366714320317257 17634 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Test that all methods are documented in POD. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2019, 2021 Russ Allbery # Copyright 2013-2014 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_automated use_prereq); use Test::RRA::Config qw(@POD_COVERAGE_EXCLUDE); use Test::More; # Skip for normal user installs since this doesn't affect functionality. skip_unless_automated('POD coverage tests'); # Load prerequisite modules. use_prereq('Test::Pod::Coverage'); # Test everything found in the distribution. all_pod_coverage_ok({ also_private => [@POD_COVERAGE_EXCLUDE] }); Tie-ShadowHash-2.01/t/docs/pod-spelling.t000555001750001750 524314320317257 17646 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Check for spelling errors in POD documentation. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2019, 2021 Russ Allbery # Copyright 2013-2014 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_author use_prereq); use Test::More; # Only run this test for the module author since the required stopwords are # too sensitive to the exact spell-checking program and dictionary. skip_unless_author('Spelling tests'); # Load prerequisite modules. use_prereq('Test::Spelling'); # Check all POD in the Perl distribution. Add the examples and t/lib # directories if they exist. Also add any files in usr/bin or usr/sbin, which # are widely used in Stanford-internal packages. my @files = all_pod_files(); if (-d 'examples') { push(@files, all_pod_files('examples')); } if (-d File::Spec->catfile('t', 'lib')) { push(@files, all_pod_files(File::Spec->catfile('t', 'lib'))); } for my $dir (qw(usr/bin usr/sbin)) { if (-d $dir) { push(@files, glob("$dir/*")); } } # We now have a list of all files to check, so output a plan and run the # tests. We can't use all_pod_files_spelling_ok because it refuses to check # non-Perl files and Stanford-internal packages have a lot of shell scripts # with POD documentation. plan tests => scalar(@files); for my $file (@files) { pod_file_spelling_ok($file); } Tie-ShadowHash-2.01/t/docs/pod.t000555001750001750 512314320317257 16030 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Check all POD documents for POD formatting errors. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2019, 2021 Russ Allbery # Copyright 2012-2014 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_automated use_prereq); use Test::More; # Skip this test for normal user installs, although pod2man may still fail. skip_unless_automated('POD syntax tests'); # Load prerequisite modules. use_prereq('Test::Pod'); # Check all POD in the Perl distribution. Add the examples and t/lib # directories if they exist. Also add any files in usr/bin or usr/sbin, # which are widely used in Stanford-internal packages. my @files = all_pod_files(); if (-d 'examples') { push(@files, all_pod_files('examples')); } if (-d File::Spec->catfile('t', 'lib')) { push(@files, all_pod_files(File::Spec->catfile('t', 'lib'))); } for my $dir (qw(usr/bin usr/sbin)) { if (-d $dir) { push(@files, glob("$dir/*")); } } # We now have a list of all files to check, so output a plan and run the # tests. We can't use all_pod_files_ok because it refuses to check non-Perl # files and Stanford-internal packages have a lot of shell scripts with POD # documentation. plan tests => scalar(@files); for my $file (@files) { pod_file_ok($file); } Tie-ShadowHash-2.01/t/docs/spdx-license.t000555001750001750 1247414320317257 17673 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Check source files for SPDX-License-Identifier fields. # # Examine all source files in a distribution to check that they contain an # SPDX-License-Identifier field. This does not check the syntax or whether # the identifiers are valid. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Copyright 2018-2021 Russ Allbery # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_automated); use File::Find qw(find); use Test::More; # File name (the file without any directory component) and path patterns to # skip for this check. ## no critic (RegularExpressions::ProhibitFixedStringMatches) #<<< my @IGNORE = ( qr{ \A Build ( [.] (?!PL) .* )? \z }ixms, # Generated file from Build.PL qr{ \A LICENSE \z }xms, # Generated file, no license itself qr{ \A (Changes|NEWS|THANKS) \z }xms, # Package license should be fine qr{ \A TODO \z }xms, # Package license should be fine qr{ \A MANIFEST ( [.] .* )? \z }xms, # Package license should be fine qr{ \A Makefile \z }xms, # Generated file, no license itself qr{ \A (MY)? META [.] .* }xms, # Generated file, no license itself qr{ [.] output \z }xms, # Test data qr{ pod2htm . [.] tmp \z }xms, # Windows pod2html output qr{ ~ \z }xms, # Backup files ); my @IGNORE_PATHS = ( qr{ \A [.] / [.] git/ }xms, # Version control files qr{ \A [.] / [.] pc/ }xms, # quilt metadata files qr{ \A [.] /_build/ }xms, # Module::Build metadata qr{ \A [.] /blib/ }xms, # Perl build system artifacts qr{ \A [.] /cover_db/ }xms, # Artifacts from coverage testing qr{ \A [.] /debian/ }xms, # Found in debian/* branches qr{ \A [.] /docs/metadata/ }xms, # Package license should be fine qr{ \A [.] /README ( [.] .* )? \z }xms, # Package license should be fine qr{ \A [.] /share/ }xms, # Package license should be fine qr{ \A [.] /t/data/generate/ }xms, # Test metadata qr{ \A [.] /t/data/spin/ }xms, # Test metadata qr{ \A [.] /t/data/update/ }xms, # Test output qr{ \A [.] /t/data .* [.] json \z }xms, # Test metadata ); #>>> ## use critic # Only run this test during automated testing, since failure doesn't indicate # any user-noticable flaw in the package itself. skip_unless_automated('SPDX identifier tests'); # Check a single file for an occurrence of the string. # # $path - Path to the file # # Returns: undef sub check_file { my $filename = $_; my $path = $File::Find::name; # Ignore files in the whitelist and binary files. for my $pattern (@IGNORE) { return if $filename =~ $pattern; } for my $pattern (@IGNORE_PATHS) { if ($path =~ $pattern) { $File::Find::prune = 1; return; } } return if -d $filename; return if !-T $filename; # Scan the file. my ($saw_legacy_notice, $saw_spdx, $skip_spdx); open(my $file, '<', $filename) or BAIL_OUT("Cannot open $path"); while (defined(my $line = <$file>)) { if ($line =~ m{ \b See \s+ LICENSE \s+ for \s+ licensing }xms) { $saw_legacy_notice = 1; } if ($line =~ m{ \b SPDX-License-Identifier: \s+ \S+ }xms) { $saw_spdx = 1; last; } if ($line =~ m{ no \s SPDX-License-Identifier \s registered }xms) { $skip_spdx = 1; last; } } close($file) or BAIL_OUT("Cannot close $path"); # If there is a legacy license notice, report a failure regardless of file # size. Otherwise, skip files under 1KB. They can be rolled up into the # overall project license and the license notice may be a substantial # portion of the file size. if ($saw_legacy_notice) { ok(!$saw_legacy_notice, "$path has legacy license notice"); } else { ok($saw_spdx || $skip_spdx || -s $filename < 1024, $path); } return; } # Use File::Find to scan all files from the top of the directory. find(\&check_file, q{.}); done_testing(); Tie-ShadowHash-2.01/t/docs/synopsis.t000555001750001750 556414320317257 17146 0ustar00eagleeagle000000000000#!/usr/bin/perl # # Check the SYNOPSIS section of the documentation for syntax errors. # # The canonical version of this file is maintained in the rra-c-util package, # which can be found at . # # Written by Russ Allbery # Copyright 2019, 2021 Russ Allbery # Copyright 2013-2014 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # SPDX-License-Identifier: MIT use 5.010; use strict; use warnings; use lib 't/lib'; use Test::RRA qw(skip_unless_automated use_prereq); use File::Spec; use Test::More; # Skip for normal user installs since this doesn't affect functionality. skip_unless_automated('Synopsis syntax tests'); # Load prerequisite modules. use_prereq('Perl::Critic::Utils'); use_prereq('Test::Synopsis'); # Helper function that checks to see if a given path starts with blib/script. # This is written a bit weirdly so that it's portable to Windows and VMS. # # $path - Path to a file # # Returns: True if the file doesn't start with blib/script, false otherwise. sub in_blib_script { my ($path) = @_; my ($volume, $dir, $file) = File::Spec->splitpath($path); my @dir = File::Spec->splitdir($dir); return (scalar(@dir) < 2 || $dir[0] ne 'blib' || $dir[1] ne 'script'); } # The default Test::Synopsis all_synopsis_ok() function requires that the # module be in a lib directory. Use Perl::Critic::Utils to find the modules # in blib, or lib if it doesn't exist. However, strip out anything in # blib/script, since scripts use a different SYNOPSIS syntax. my @files = Perl::Critic::Utils::all_perl_files('blib'); @files = grep { in_blib_script($_) } @files; if (!@files) { @files = Perl::Critic::Utils::all_perl_files('lib'); } plan tests => scalar @files; # Run the actual tests. for my $file (@files) { synopsis_ok($file); } Tie-ShadowHash-2.01/t/lib000755001750001750 014320317257 14536 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/lib/Test000755001750001750 014320317257 15455 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/lib/Test/RRA.pm000444001750001750 2562714320317257 16630 0ustar00eagleeagle000000000000# Helper functions for test programs written in Perl. # # This module provides a collection of helper functions used by test programs # written in Perl. This is a general collection of functions that can be used # by both C packages with Automake and by stand-alone Perl modules. See # Test::RRA::Automake for additional functions specifically for C Automake # distributions. # # SPDX-License-Identifier: MIT package Test::RRA; use 5.010; use base qw(Exporter); use strict; use warnings; use Carp qw(croak); use File::Temp; # Abort if Test::More was loaded before Test::RRA to be sure that we get the # benefits of the Test::More probing below. if ($INC{'Test/More.pm'}) { croak('Test::More loaded before Test::RRA'); } # Red Hat's base perl package doesn't include Test::More (one has to install # the perl-core package in addition). Try to detect this and skip any Perl # tests if Test::More is not present. This relies on Test::RRA being included # before Test::More. eval { require Test::More; Test::More->import(); }; if ($@) { print "1..0 # SKIP Test::More required for test\n" or croak('Cannot write to stdout'); exit 0; } # Declare variables that should be set in BEGIN for robustness. our (@EXPORT_OK, $VERSION); # Set $VERSION and everything export-related in a BEGIN block for robustness # against circular module loading (not that we load any modules, but # consistency is good). BEGIN { @EXPORT_OK = qw( is_file_contents skip_unless_author skip_unless_automated use_prereq ); # This version should match the corresponding rra-c-util release, but with # two digits for the minor version, including a leading zero if necessary, # so that it will sort properly. $VERSION = '10.02'; } # Compare a string to the contents of a file, similar to the standard is() # function, but to show the line-based unified diff between them if they # differ. # # $got - The output that we received # $expected - The path to the file containing the expected output # $message - The message to use when reporting the test results # # Returns: undef # Throws: Exception on failure to read or write files or run diff sub is_file_contents { my ($got, $expected, $message) = @_; # If they're equal, this is simple. open(my $fh, '<', $expected) or BAIL_OUT("Cannot open $expected: $!\n"); my $data = do { local $/ = undef; <$fh> }; close($fh) or BAIL_OUT("Cannot close $expected: $!\n"); if ($got eq $data) { is($got, $data, $message); return; } # Otherwise, we show a diff, but only if we have IPC::System::Simple and # diff succeeds. Otherwise, we fall back on showing the full expected and # seen output. eval { require IPC::System::Simple; my $tmp = File::Temp->new(); my $tmpname = $tmp->filename; print {$tmp} $got or BAIL_OUT("Cannot write to $tmpname: $!\n"); my @command = ('diff', '-u', $expected, $tmpname); my $diff = IPC::System::Simple::capturex([0 .. 1], @command); diag($diff); }; if ($@) { diag('Expected:'); diag($expected); diag('Seen:'); diag($data); } # Report failure. ok(0, $message); return; } # Skip this test unless author tests are requested. Takes a short description # of what tests this script would perform, which is used in the skip message. # Calls plan skip_all, which will terminate the program. # # $description - Short description of the tests # # Returns: undef sub skip_unless_author { my ($description) = @_; if (!$ENV{AUTHOR_TESTING}) { plan(skip_all => "$description only run for author"); } return; } # Skip this test unless doing automated testing or release testing. This is # used for tests that should be run by CPAN smoke testing or during releases, # but not for manual installs by end users. Takes a short description of what # tests this script would perform, which is used in the skip message. Calls # plan skip_all, which will terminate the program. # # $description - Short description of the tests # # Returns: undef sub skip_unless_automated { my ($description) = @_; for my $env (qw(AUTOMATED_TESTING RELEASE_TESTING AUTHOR_TESTING)) { return if $ENV{$env}; } plan(skip_all => "$description normally skipped"); return; } # Attempt to load a module and skip the test if the module could not be # loaded. If the module could be loaded, call its import function manually. # If the module could not be loaded, calls plan skip_all, which will terminate # the program. # # The special logic here is based on Test::More and is required to get the # imports to happen in the caller's namespace. # # $module - Name of the module to load # @imports - Any arguments to import, possibly including a version # # Returns: undef sub use_prereq { my ($module, @imports) = @_; # If the first import looks like a version, pass it as a bare string. my $version = q{}; if (@imports >= 1 && $imports[0] =~ m{ \A \d+ (?: [.][\d_]+ )* \z }xms) { $version = shift(@imports); } # Get caller information to put imports in the correct package. my ($package) = caller; # Do the import with eval, and try to isolate it from the surrounding # context as much as possible. Based heavily on Test::More::_eval. ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines) my ($result, $error, $sigdie); { local $@ = undef; local $! = undef; local $SIG{__DIE__} = undef; $result = eval qq{ package $package; use $module $version \@imports; 1; }; $error = $@; $sigdie = $SIG{__DIE__} || undef; } # If the use failed for any reason, skip the test. if (!$result || $error) { my $name = length($version) > 0 ? "$module $version" : $module; plan(skip_all => "$name required for test"); } # If the module set $SIG{__DIE__}, we cleared that via local. Restore it. ## no critic (Variables::RequireLocalizedPunctuationVars) if (defined($sigdie)) { $SIG{__DIE__} = $sigdie; } return; } 1; __END__ =for stopwords Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util CPAN diff =head1 NAME Test::RRA - Support functions for Perl tests =head1 SYNOPSIS use Test::RRA qw(skip_unless_author skip_unless_automated use_prereq); # Skip this test unless author tests are requested. skip_unless_author('Coding style tests'); # Skip this test unless doing automated or release testing. skip_unless_automated('POD syntax tests'); # Load modules, skipping the test if they're not available. use_prereq('Perl6::Slurp', 'slurp'); use_prereq('Test::Script::Run', '0.04'); =head1 DESCRIPTION This module collects utility functions that are useful for Perl test scripts. It assumes Russ Allbery's Perl module layout and test conventions and will only be useful for other people if they use the same conventions. This module B be loaded before Test::More or it will abort during import. It will skip the test (by printing a skip message to standard output and exiting with status 0, equivalent to C) during import if Test::More is not available. This allows tests written in Perl using this module to be skipped if run on a system with Perl but not Test::More, such as Red Hat systems with the C package but not the C package installed. =head1 FUNCTIONS None of these functions are imported by default. The ones used by a script should be explicitly imported. =over 4 =item is_file_contents(GOT, EXPECTED, MESSAGE) Check a string against the contents of a file, showing the differences if any using diff (if IPC::System::Simple and diff are available). GOT is the output the test received. EXPECTED is the path to a file containing the expected output (not the output itself). MESSAGE is a message to display alongside the test results. =item skip_unless_author(DESC) Checks whether AUTHOR_TESTING is set in the environment and skips the whole test (by calling C from Test::More) if it is not. DESC is a description of the tests being skipped. A space and C will be appended to it and used as the skip reason. =item skip_unless_automated(DESC) Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are set in the environment and skips the whole test (by calling C from Test::More) if they are not. This should be used by tests that should not run during end-user installs of the module, but which should run as part of CPAN smoke testing and release testing. DESC is a description of the tests being skipped. A space and C will be appended to it and used as the skip reason. =item use_prereq(MODULE[, VERSION][, IMPORT ...]) Attempts to load MODULE with the given VERSION and import arguments. If this fails for any reason, the test will be skipped (by calling C from Test::More) with a skip reason saying that MODULE is required for the test. VERSION will be passed to C as a version bareword if it looks like a version number. The remaining IMPORT arguments will be passed as the value of an array. =back =head1 AUTHOR Russ Allbery =head1 COPYRIGHT AND LICENSE Copyright 2016, 2018-2019, 2021 Russ Allbery Copyright 2013-2014 The Board of Trustees of the Leland Stanford Junior University Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =head1 SEE ALSO Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3) This module is maintained in the rra-c-util package. The current version is available from L. The functions to control when tests are run use environment variables defined by the L. =cut # Local Variables: # copyright-at-end-flag: t # End: Tie-ShadowHash-2.01/t/lib/Test/RRA000755001750001750 014320317257 16101 5ustar00eagleeagle000000000000Tie-ShadowHash-2.01/t/lib/Test/RRA/Config.pm000444001750001750 1735114320317257 20030 0ustar00eagleeagle000000000000# Configuration for Perl test cases. # # In order to reuse the same Perl test cases in multiple packages, I use a # configuration file to store some package-specific data. This module loads # that configuration and provides the namespace for the configuration # settings. # # SPDX-License-Identifier: MIT package Test::RRA::Config; use 5.010; use base qw(Exporter); use strict; use warnings; use Test::More; # Declare variables that should be set in BEGIN for robustness. our (@EXPORT_OK, $VERSION); # Set $VERSION and everything export-related in a BEGIN block for robustness # against circular module loading (not that we load any modules, but # consistency is good). BEGIN { @EXPORT_OK = qw( $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH $MINIMUM_VERSION %MINIMUM_VERSION @MODULE_VERSION_IGNORE @POD_COVERAGE_EXCLUDE @STRICT_IGNORE @STRICT_PREREQ ); # This version should match the corresponding rra-c-util release, but with # two digits for the minor version, including a leading zero if necessary, # so that it will sort properly. $VERSION = '10.02'; } # If C_TAP_BUILD or C_TAP_SOURCE are set in the environment, look for # data/perl.conf under those paths for a C Automake package. Otherwise, look # in t/data/perl.conf for a standalone Perl module or tests/data/perl.conf for # Perl tests embedded in a larger distribution. Don't use Test::RRA::Automake # since it may not exist. our $PATH; for my $base ($ENV{C_TAP_BUILD}, $ENV{C_TAP_SOURCE}, './t', './tests') { next if !defined($base); my $path = "$base/data/perl.conf"; if (-r $path) { $PATH = $path; last; } } if (!defined($PATH)) { BAIL_OUT('cannot find data/perl.conf'); } # Pre-declare all of our variables and set any defaults. our $COVERAGE_LEVEL = 100; our @COVERAGE_SKIP_TESTS; our @CRITIC_IGNORE; our $LIBRARY_PATH; our $MINIMUM_VERSION = '5.010'; our %MINIMUM_VERSION; our @MODULE_VERSION_IGNORE; our @POD_COVERAGE_EXCLUDE; our @STRICT_IGNORE; our @STRICT_PREREQ; # Load the configuration. if (!do($PATH)) { my $error = $@ || $! || 'loading file did not return true'; BAIL_OUT("cannot load $PATH: $error"); } 1; __END__ =for stopwords Allbery rra-c-util Automake perlcritic .libs namespace subdirectory sublicense MERCHANTABILITY NONINFRINGEMENT regexes =head1 NAME Test::RRA::Config - Perl test configuration =head1 SYNOPSIS use Test::RRA::Config qw($MINIMUM_VERSION); print "Required Perl version is $MINIMUM_VERSION\n"; =head1 DESCRIPTION Test::RRA::Config encapsulates per-package configuration for generic Perl test programs that are shared between multiple packages using the rra-c-util infrastructure. It handles locating and loading the test configuration file for both C Automake packages and stand-alone Perl modules. Test::RRA::Config looks for a file named F relative to the root of the test directory. That root is taken from the environment variables C_TAP_BUILD or C_TAP_SOURCE (in that order) if set, which will be the case for C Automake packages using C TAP Harness. If neither is set, it expects the root of the test directory to be a directory named F relative to the current directory, which will be the case for stand-alone Perl modules. The following variables are supported: =over 4 =item $COVERAGE_LEVEL The coverage level achieved by the test suite for Perl test coverage testing using Test::Strict, as a percentage. The test will fail if test coverage less than this percentage is achieved. If not given, defaults to 100. =item @COVERAGE_SKIP_TESTS Directories under F whose tests should be skipped when doing coverage testing. This can be tests that won't contribute to coverage or tests that don't run properly under Devel::Cover for some reason (such as ones that use taint checking). F and F