From 22d921a6e9c4fc98cda05e898a8137c7e8dae970 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Sat, 1 Nov 2014 18:05:53 +0000 Subject: Devel-StackTrace-2.00 --- Changes | 336 ++++++++++++++++++++++ INSTALL | 43 +++ LICENSE | 207 ++++++++++++++ MANIFEST | 35 +++ META.json | 650 ++++++++++++++++++++++++++++++++++++++++++ META.yml | 474 ++++++++++++++++++++++++++++++ Makefile.PL | 73 +++++ README.md | 239 ++++++++++++++++ dist.ini | 13 + lib/Devel/StackTrace.pm | 531 ++++++++++++++++++++++++++++++++++ lib/Devel/StackTrace/Frame.pm | 216 ++++++++++++++ t/00-compile.t | 52 ++++ t/00-report-prereqs.dd | 46 +++ t/00-report-prereqs.t | 176 ++++++++++++ t/01-basic.t | 573 +++++++++++++++++++++++++++++++++++++ t/02-bad-utf8.t | 40 +++ t/03-message.t | 34 +++ t/04-indent.t | 35 +++ t/05-back-compat.t | 10 + t/06-dollar-at.t | 24 ++ t/07-no-args.t | 45 +++ t/08-filter-early.t | 31 ++ t/09-skip-frames.t | 41 +++ t/author-eol.t | 16 ++ t/author-no-tabs.t | 45 +++ t/author-pod-spell.t | 45 +++ t/release-cpan-changes.t | 19 ++ t/release-pod-coverage.t | 43 +++ t/release-pod-linkcheck.t | 28 ++ t/release-pod-no404s.t | 29 ++ t/release-pod-syntax.t | 14 + t/release-portability.t | 19 ++ t/release-synopsis.t | 13 + weaver.ini | 17 ++ 34 files changed, 4212 insertions(+) create mode 100644 Changes create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 MANIFEST create mode 100644 META.json create mode 100644 META.yml create mode 100644 Makefile.PL create mode 100644 README.md create mode 100644 dist.ini create mode 100644 lib/Devel/StackTrace.pm create mode 100644 lib/Devel/StackTrace/Frame.pm create mode 100644 t/00-compile.t create mode 100644 t/00-report-prereqs.dd create mode 100644 t/00-report-prereqs.t create mode 100644 t/01-basic.t create mode 100644 t/02-bad-utf8.t create mode 100644 t/03-message.t create mode 100644 t/04-indent.t create mode 100644 t/05-back-compat.t create mode 100644 t/06-dollar-at.t create mode 100644 t/07-no-args.t create mode 100644 t/08-filter-early.t create mode 100644 t/09-skip-frames.t create mode 100644 t/author-eol.t create mode 100644 t/author-no-tabs.t create mode 100644 t/author-pod-spell.t create mode 100644 t/release-cpan-changes.t create mode 100644 t/release-pod-coverage.t create mode 100644 t/release-pod-linkcheck.t create mode 100644 t/release-pod-no404s.t create mode 100644 t/release-pod-syntax.t create mode 100644 t/release-portability.t create mode 100644 t/release-synopsis.t create mode 100644 weaver.ini diff --git a/Changes b/Changes new file mode 100644 index 0000000..3170913 --- /dev/null +++ b/Changes @@ -0,0 +1,336 @@ +2.00 2014-11-01 + +[BACKWARDS INCOMPATIBILITIES] + +- The no_refs constructor parameter is now deprecated, and has been replace by + a new unsafe_ref_capture parameter that defaults to false, meaning no + references are captured by default. Capturing references by default caused + too many issues that couldn't be worked around, including running DESTROY + blocks multiple times on captured objects in the worst case. + +- Removed support for the long-deprecated no_object_refs constructor parameter + (deprecated in 2002!). + + +1.34 2014-06-26 + +- Fixed use of // operator (my use, not Graham's) in previous release. + + +1.33 2014-06-26 + +- Added a skip_frames option. This causes the stack trace to skip an arbitrary + number of frames. Patch by Graham Knopp. PR #5. + + +1.32 2014-05-05 + +- Added a filter_frames_early option to filter frames before arguments are + stringified. Added by Dagfinn Ilmari Mannsåker. PR #4. + + +1.31 2014-01-16 + +- No code changes, just doc updates, including documenting the as_string() + method in Devel::StackTrace::Frame. Requested by Skef. RT #91575. + + +1.30 2012-11-19 + +- There was an eval which did not first localize $@ and $SIG{__DIE__}. This + broke Plack::Middleware::StackTrace (and possibly other tihngs). + + +1.29 2012-11-16 + +- The Devel::StackTrace->frames() method is now read-write. This allows you to + do more complex filtering of frames than is easily possible with the + frame_filter argument to the constructor. Patch by David Cantrell. + + +1.28 2012-11-16 + +- Allow arguments to a trace's as_string method, specifically max_arg_length + Patch by Ricardo Signes. + +- Added a no_args option to the constructor in 1.26 but forgot to mention it + in Changes. Requested by Scott J. Miller. RT #71482. + + +1.27 2011-01-16 + +- Skip some tests on 5.13.8+ that are no longer relevant because of a change + in the Perl core. Reported by Andreas Koenig. RT #64828. + + +1.26 2010-10-15 + +- The as_string method did not localize $@ and $SIG{__DIE__} before doing an + eval. Reported and tested by Marc Mims. RT #61072. + + +1.25 2010-09-06 + +- Devel::StackTraceFrame was not actually subclassing + Devel::StackTrace::Frame. Patch by Tatsuhiko Miyagawa. + + +1.24 2010-09-03 + +- Version 1.23 was missing a $VERSION assignment. Reported by Sergei + Vyshenski. + +- Moved the frame object to its own file, and renamed it + Devel::StackTrace::Frame. The old package name, Devel::StackTraceFrame, is + now a subclass of the new package, to provide a backwards compatibility + shim. + + +1.23 2010-08-27 + +- Added message and indent constructor parameters. Based on a patch by James + Laver. RT #59830. + + +1.22 2009-07-15 + +- Apparently, overload::StrVal on older Perls (5.8.5, but not 5.8.8) + tried to call a stringification method if it existed. So now, + Devel::StackTrace just uses overload::AddrRef instead, which should + always be safe. Reported by Michael Stevens. Fixes RT #47900. + + +1.21 2009-07-01 + +- Overloaded objects which didn't provide a stringification method + cause Devel::StackTrace to die when respect_overload was + true. Reported by Laurent Dami. RT #39533. + +- Added a frame_filter option which allows for fine-grained control + over what frames are included in a trace. Based on (but expanded) + from a patch proposed by Florian Ragwitz. RT #47415. + + +1.20 2008-10-25 + +- The change in 1.15 to object creation broke the no_refs feature, + causing references to be stored until the trace's frame objects were + created. + +* Exception::Class objects are always stringified by calling + overload::StrVal(). + + +1.1902 2008-07-16 + +- This release just contains another test fix. + +- The new tests for bad utf-8 apparently fail with any Perl before + 5.8.8. Reported by Lee Heagney. RT #37702. + + +1.1901 2008-06-13 + +- This release just contains a test fix. + +- The new tests for bad utf-8 fail with Perl 5.8.x where x <= + 6. Apparently, utf-8 was just more broken back then. Reported by + Andreas Koenig's smokebots. + + +1.19 2008-06-13 + +- Dropped support for Perl 5.005. + +- If a function was in stack trace had been called with invalid utf-8 + bytes, this could cause stringifying a stack trace to blow up when + it tried to stringify that argument. We now catch those (and other) + errors and simply put "(bad utf-8)" or "?" in the stringified + argument list. Reported by Alex Vandiver. + + +1.18 2008-03-31 + +- Fix a test failure on Win32. No changes to the non-test code. + + +1.17 2008-03-30 + +- Added a max_arg_length parameter, which if set causes + Devel::StackTrace to truncate long strings when printing out a + frame. RT #33519. Patch by Ian Burrell. + + +1.16 2008-02-02 + +- A test fix for bleadperl. The value of wantarray from caller() needs + to be treated as a boolean, as opposed to expecting 0 (vs + undef). RT #32583. Patch by Jerry Hedden. + + +1.15 2007-04-28 + +- Changed how objects are created in order to greatly speed up the + constructor. Instead of processing all the stack trace data when the + object is first created, this is delayed until it is needed. This + was done in order to help speed up Exception::Class. There are cases + where code may be throwing many exceptions but never examining the + stack traces. + + Here is a representative benchmark of object construction for the + old code versus the new code: + + Rate old new + old 1764/s -- -76% + new 7353/s 317% -- + + +1.14 2007-03-16 + +- Added a few micro-optimizations from Ruslan Zakirov, who is hoping + this will ultimately help speed up RT. + + +1.13 2006-04-01 + +- Add another fix for filename handling in the tests. Tests were + giving false failures on Win32 because the tests needed to use + File::Spec->canonpath(), just like Devel::StackTrace does + internally. + + +1.12 2005-09-30 + +- Newer versions of Perl use Unix-style filenames when reporting the + filename in caller(), which breaks Exception::Class tests on other + platforms, and is just kind of funky. This module now calls + File::Spec->canonpath() to clean up the filename in each frame. + Reported by Garret Goebel. + + +1.11 2004-04-12 + +- No code changes, just switching to including a Makefile.PL that uses + ExtUtils::MakeMaker instead of one that sneakily uses Module::Build. + Requested by Perrin Harkins. + + +1.10 2004-03-10 + +- Silence a warning from the test code if Exception::Class isn't + installed. Reported by Stefano Ruberti. + +- Localize $@ to avoid overwriting a previously set $@ while creating + a Devel::StackTrace object. This caused a test failure in the + Exception::Class tests when run with Perl 5.6.1, but not with 5.8.3. + I don't really know how to test for it outside of Exception::Class. + Reported by Jesse Erlbaum. + + +1.09 2004-02-26 + +- The overload workaround blows up if a DBI handle is anywhere in the + stack, because of a bad interaction between overload::Overloaded and + DBI's custom dispatching. This release works around that. + + +1.08 2004-02-23 + +- Some tests failed on Win32 because they were hardcoded to expect a + file name with forward slashes. Reported by Steve Hay. + + +1.07 2004-02-21 + +- This release includes a change to the overload handling that is + necessary for cooperation with Exception::Class. + + +1.06 2004-02-21 + +- Devel::StackTrace now uses overload::StrVal() to get the underlying + string value of an overloaded object when creating a stack frame for + display. This can be turned off by setting respect_overload to a + true value. Suggested by Matt Sisk. + + +1.05 2004-02-17 + +- Devel::StackTrace incorrectly reported that arguments were being + passed to eval blocks (which isn't possible). Reported by Mark + Dedlow. + + +1.04 2003-09-25 + +- The special handling of Exception::Class::Base objects was broken. + This was exposed by the fact that Exception::Class 1.15 now uses + Devel::StackTrace in a slightly different way than it did + previously. + + +1.03 2003-01-22 + +- Special handling of Exception::Class::Base objects when stringifying + references. This avoids infinite recursion between the two classes. + + +1.02 2002-09-19 + +- Forgot to add Test::More to PREREQ_PM for previous releases. + + +1.01 2002-09-18 + +- Change the "no object refs" feature to be a plain old "no refs" + feature. As was pointed out to me by Jean-Phillippe Bouchard, a + plain reference (to an array, for example), can easily hold + references to objects internally. And since I'm not going to bother + descending through nested data structures weeding out objects, this + is an easier way to handle the problem. Thanks to Jean-Phillippe + Bouchard for a patch for this as well. + + The "no_object_refs" parameter is deprecated, and now does the same + thing as the "no_refs" parameter. + + +1.00 2010-10-15 + +- Add an option to not store references to objects in stack frames. + This can be important if you're expecting DESTROY to be called but a + Devel::StackTraceFrame object is still holding a reference to your + object(s). Based on discussion with Tatsuhiko Miyagawa. + + +0.9 2001-11-24 + +- Doc tweaks. + + +0.85 2000-09-02 + +- doc bug fix that made it seem like args method was only available + under Perl 5.6.0 + +- converted objects from pseudo-hashes to regular hashes. + + +0.8 2000-09-02 + +- Should work under Perl 5.6.0+. + +- Added hints & bitmask methods for use under Perl 5.6.0. + + +0.75 2000-06-29 + +- Added frames method (and docs for it). + +- Added 'use 5.005' which I should have put in there earlier. + +- DOCS: explanation of 'top' and 'bottom' as they refer to the stack. + + +0.7 2000-06-27 + +- First release (I think) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..8abeb88 --- /dev/null +++ b/INSTALL @@ -0,0 +1,43 @@ +This is the Perl distribution Devel-StackTrace. + +Installing Devel-StackTrace is straightforward. + +## Installation with cpanm + +If you have cpanm, you only need one line: + + % cpanm Devel::StackTrace + +If you are installing into a system-wide directory, you may need to pass the +"-S" flag to cpanm, which uses sudo to install the module: + + % cpanm -S Devel::StackTrace + +## Installing with the CPAN shell + +Alternatively, if your CPAN shell is set up, you should just be able to do: + + % cpan Devel::StackTrace + +## Manual installation + +As a last resort, you can manually install it. Download the tarball, untar it, +then build it: + + % perl Makefile.PL + % make && make test + +Then install it: + + % make install + +If you are installing into a system-wide directory, you may need to run: + + % sudo make install + +## Documentation + +Devel-StackTrace documentation is available as POD. +You can run perldoc from a shell to read the documentation: + + % perldoc Devel::StackTrace diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8693142 --- /dev/null +++ b/LICENSE @@ -0,0 +1,207 @@ +This software is Copyright (c) 2000 - 2014 by David Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) + + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..6d8d21e --- /dev/null +++ b/MANIFEST @@ -0,0 +1,35 @@ +# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.023. +Changes +INSTALL +LICENSE +MANIFEST +META.json +META.yml +Makefile.PL +README.md +dist.ini +lib/Devel/StackTrace.pm +lib/Devel/StackTrace/Frame.pm +t/00-compile.t +t/00-report-prereqs.dd +t/00-report-prereqs.t +t/01-basic.t +t/02-bad-utf8.t +t/03-message.t +t/04-indent.t +t/05-back-compat.t +t/06-dollar-at.t +t/07-no-args.t +t/08-filter-early.t +t/09-skip-frames.t +t/author-eol.t +t/author-no-tabs.t +t/author-pod-spell.t +t/release-cpan-changes.t +t/release-pod-coverage.t +t/release-pod-linkcheck.t +t/release-pod-no404s.t +t/release-pod-syntax.t +t/release-portability.t +t/release-synopsis.t +weaver.ini diff --git a/META.json b/META.json new file mode 100644 index 0000000..fb878c0 --- /dev/null +++ b/META.json @@ -0,0 +1,650 @@ +{ + "abstract" : "An object representing a stack trace", + "author" : [ + "Dave Rolsky " + ], + "dynamic_config" : 0, + "generated_by" : "Dist::Zilla version 5.023, CPAN::Meta::Converter version 2.142690", + "license" : [ + "artistic_2" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "Devel-StackTrace", + "prereqs" : { + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0" + } + }, + "develop" : { + "requires" : { + "Pod::Coverage::TrustPod" : "0", + "Test::CPAN::Changes" : "0.19", + "Test::EOL" : "0", + "Test::More" : "0.88", + "Test::NoTabs" : "0", + "Test::Pod" : "1.41", + "Test::Pod::Coverage" : "1.08", + "Test::Spelling" : "0.12", + "Test::Synopsis" : "0" + } + }, + "runtime" : { + "requires" : { + "File::Spec" : "0", + "Scalar::Util" : "0", + "overload" : "0", + "perl" : "5.006", + "strict" : "0", + "warnings" : "0" + } + }, + "test" : { + "recommends" : { + "CPAN::Meta" : "2.120900" + }, + "requires" : { + "ExtUtils::MakeMaker" : "0", + "File::Spec" : "0", + "IO::Handle" : "0", + "IPC::Open3" : "0", + "Test::More" : "0.88", + "base" : "0", + "bytes" : "0" + } + } + }, + "provides" : { + "Devel::StackTrace" : { + "file" : "lib/Devel/StackTrace.pm", + "version" : "2.00" + }, + "Devel::StackTrace::Frame" : { + "file" : "lib/Devel/StackTrace/Frame.pm", + "version" : "2.00" + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "mailto" : "bug-devel-stacktrace@rt.cpan.org", + "web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=Devel-StackTrace" + }, + "homepage" : "http://metacpan.org/release/Devel-StackTrace", + "repository" : { + "type" : "git", + "url" : "git://github.com/autarch/Devel-StackTrace.git", + "web" : "https://github.com/autarch/Devel-StackTrace" + } + }, + "version" : "2.00", + "x_Dist_Zilla" : { + "perl" : { + "version" : "5.016003" + }, + "plugins" : [ + { + "class" : "Dist::Zilla::Plugin::MakeMaker", + "config" : { + "Dist::Zilla::Role::TestRunner" : { + "default_jobs" : 1 + } + }, + "name" : "@DROLSKY/MakeMaker", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::Authority", + "name" : "@DROLSKY/Authority", + "version" : "1.009" + }, + { + "class" : "Dist::Zilla::Plugin::AutoPrereqs", + "name" : "@DROLSKY/AutoPrereqs", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::GatherDir", + "config" : { + "Dist::Zilla::Plugin::GatherDir" : { + "exclude_filename" : [ + "README.md" + ], + "exclude_match" : [], + "follow_symlinks" : "0", + "include_dotfiles" : "0", + "prefix" : "", + "prune_directory" : [], + "root" : "." + } + }, + "name" : "@DROLSKY/GatherDir", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::GitHub::Meta", + "name" : "@DROLSKY/GitHub::Meta", + "version" : "0.38" + }, + { + "class" : "Dist::Zilla::Plugin::GitHub::Update", + "name" : "@DROLSKY/GitHub::Update", + "version" : "0.38" + }, + { + "class" : "Dist::Zilla::Plugin::MetaResources", + "name" : "@DROLSKY/MetaResources", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::MetaProvides::Package", + "config" : { + "Dist::Zilla::Plugin::MetaProvides::Package" : { + "finder_objects" : [ + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : "@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM", + "version" : "5.023" + } + ] + }, + "Dist::Zilla::Role::MetaProvider::Provider" : { + "inherit_missing" : "1", + "inherit_version" : "1", + "meta_noindex" : "1" + } + }, + "name" : "@DROLSKY/MetaProvides::Package", + "version" : "2.001002" + }, + { + "class" : "Dist::Zilla::Plugin::NextRelease", + "name" : "@DROLSKY/NextRelease", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::PkgVersion", + "name" : "@DROLSKY/PkgVersion", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::Prereqs", + "config" : { + "Dist::Zilla::Plugin::Prereqs" : { + "phase" : "test", + "type" : "requires" + } + }, + "name" : "@DROLSKY/TestMoreDoneTesting", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::PromptIfStale", + "config" : { + "Dist::Zilla::Plugin::PromptIfStale" : { + "check_all_plugins" : "1", + "check_all_prereqs" : "1", + "modules" : [], + "phase" : "release", + "skip" : [] + } + }, + "name" : "@DROLSKY/stale modules, release", + "version" : "0.028" + }, + { + "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", + "name" : "@DROLSKY/ReadmeMarkdownInBuild", + "version" : "0.142470" + }, + { + "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", + "name" : "@DROLSKY/ReadmeMarkdownInRoot", + "version" : "0.142470" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable", + "name" : "@DROLSKY/Test::Pod::Coverage::Configurable", + "version" : "0.01" + }, + { + "class" : "Dist::Zilla::Plugin::Test::PodSpelling", + "name" : "@DROLSKY/Test::PodSpelling", + "version" : "2.006008" + }, + { + "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", + "name" : "@DROLSKY/Test::ReportPrereqs", + "version" : "0.019" + }, + { + "class" : "Dist::Zilla::Plugin::PruneCruft", + "name" : "@DROLSKY/PruneCruft", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::ManifestSkip", + "name" : "@DROLSKY/ManifestSkip", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::MetaYAML", + "name" : "@DROLSKY/MetaYAML", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::License", + "name" : "@DROLSKY/License", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::ExtraTests", + "name" : "@DROLSKY/ExtraTests", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::ExecDir", + "name" : "@DROLSKY/ExecDir", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::ShareDir", + "name" : "@DROLSKY/ShareDir", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::Manifest", + "name" : "@DROLSKY/Manifest", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::TestRelease", + "name" : "@DROLSKY/TestRelease", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::ConfirmRelease", + "name" : "@DROLSKY/ConfirmRelease", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::UploadToCPAN", + "name" : "@DROLSKY/UploadToCPAN", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", + "name" : "@DROLSKY/CheckPrereqsIndexed", + "version" : "0.012" + }, + { + "class" : "Dist::Zilla::Plugin::CopyReadmeFromBuild", + "name" : "@DROLSKY/CopyReadmeFromBuild", + "version" : "0.0019" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::Contributors", + "name" : "@DROLSKY/DROLSKY::Contributors", + "version" : "0.21" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::License", + "name" : "@DROLSKY/DROLSKY::License", + "version" : "0.21" + }, + { + "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch", + "config" : { + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::CheckFor::CorrectBranch", + "version" : "0.011" + }, + { + "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts", + "config" : { + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::CheckFor::MergeConflicts", + "version" : "0.011" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Contributors", + "config" : { + "Dist::Zilla::Plugin::Git::Contributors" : { + "include_authors" : "0", + "include_releaser" : "1", + "order_by" : "name", + "paths" : [] + } + }, + "name" : "@DROLSKY/Git::Contributors", + "version" : "0.008" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Describe", + "name" : "@DROLSKY/Git::Describe", + "version" : "0.003" + }, + { + "class" : "Dist::Zilla::Plugin::InstallGuide", + "name" : "@DROLSKY/InstallGuide", + "version" : "1.200005" + }, + { + "class" : "Dist::Zilla::Plugin::Meta::Contributors", + "name" : "@DROLSKY/Meta::Contributors", + "version" : "0.001" + }, + { + "class" : "Dist::Zilla::Plugin::MetaConfig", + "name" : "@DROLSKY/MetaConfig", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::MetaJSON", + "name" : "@DROLSKY/MetaJSON", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::SurgicalPodWeaver", + "config" : { + "Dist::Zilla::Plugin::PodWeaver" : { + "finder" : [ + ":InstallModules", + ":ExecFiles" + ], + "plugins" : [ + { + "class" : "Pod::Weaver::Plugin::EnsurePod5", + "name" : "@CorePrep/EnsurePod5", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Plugin::H1Nester", + "name" : "@CorePrep/H1Nester", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Name", + "name" : "Name", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Version", + "name" : "Version", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "prelude", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Generic", + "name" : "SYNOPSIS", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Generic", + "name" : "DESCRIPTION", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Leftovers", + "name" : "Leftovers", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "postlude", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Authors", + "name" : "Authors", + "version" : "4.006" + }, + { + "class" : "Pod::Weaver::Section::Contributors", + "name" : "Contributors", + "version" : "0.008" + }, + { + "class" : "Pod::Weaver::Section::Legal", + "name" : "Legal", + "version" : "4.006" + } + ] + } + }, + "name" : "@DROLSKY/SurgicalPodWeaver", + "version" : "0.0021" + }, + { + "class" : "Dist::Zilla::Plugin::PodSyntaxTests", + "name" : "@DROLSKY/PodSyntaxTests", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes", + "name" : "@DROLSKY/Test::CPAN::Changes", + "version" : "0.008" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Compile", + "config" : { + "Dist::Zilla::Plugin::Test::Compile" : { + "bail_out_on_fail" : "0", + "fail_on_warning" : "author", + "fake_home" : "0", + "filename" : "t/00-compile.t", + "module_finder" : [ + ":InstallModules" + ], + "needs_display" : "0", + "phase" : "test", + "script_finder" : [ + ":ExecFiles" + ], + "skips" : [] + } + }, + "name" : "@DROLSKY/Test::Compile", + "version" : "2.051" + }, + { + "class" : "Dist::Zilla::Plugin::Test::EOL", + "config" : { + "Dist::Zilla::Plugin::Test::EOL" : { + "filename" : "xt/author/eol.t" + } + }, + "name" : "@DROLSKY/Test::EOL", + "version" : "0.15" + }, + { + "class" : "Dist::Zilla::Plugin::Test::NoTabs", + "config" : { + "Dist::Zilla::Plugin::Test::NoTabs" : { + "filename" : "xt/author/no-tabs.t", + "finder" : [ + ":InstallModules", + ":ExecFiles", + ":TestFiles" + ] + } + }, + "name" : "@DROLSKY/Test::NoTabs", + "version" : "0.09" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Pod::LinkCheck", + "name" : "@DROLSKY/Test::Pod::LinkCheck", + "version" : "1.001" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Pod::No404s", + "name" : "@DROLSKY/Test::Pod::No404s", + "version" : "1.001" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Portability", + "name" : "@DROLSKY/Test::Portability", + "version" : "2.000005" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Synopsis", + "name" : "@DROLSKY/Test::Synopsis", + "version" : "2.000005" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Check", + "config" : { + "Dist::Zilla::Plugin::Git::Check" : { + "untracked_files" : "die" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "Changes", + "CONTRIBUTING.md", + "README.md" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Check", + "version" : "2.025" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Commit", + "config" : { + "Dist::Zilla::Plugin::Git::Commit" : { + "add_files_in" : [], + "commit_msg" : "v%v%n%n%c", + "time_zone" : "local" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "Changes", + "CONTRIBUTING.md", + "README.md" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Commit", + "version" : "2.025" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Tag", + "config" : { + "Dist::Zilla::Plugin::Git::Tag" : { + "branch" : null, + "signed" : 0, + "tag" : "v2.00", + "tag_format" : "v%v", + "tag_message" : "v%v", + "time_zone" : "local" + }, + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Tag", + "version" : "2.025" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Push", + "config" : { + "Dist::Zilla::Plugin::Git::Push" : { + "push_to" : [ + "origin" + ], + "remotes_must_exist" : 1 + }, + "Dist::Zilla::Role::Git::Repo" : { + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Push", + "version" : "2.025" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":InstallModules", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":IncModules", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":TestFiles", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ExecFiles", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ShareFiles", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":MainModule", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":AllFiles", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":NoFiles", + "version" : "5.023" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : "@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM", + "version" : "5.023" + } + ], + "zilla" : { + "class" : "Dist::Zilla::Dist::Builder", + "config" : { + "is_trial" : "0" + }, + "version" : "5.023" + } + }, + "x_authority" : "cpan:DROLSKY", + "x_contributors" : [ + "Dagfinn Ilmari Mannsåker ", + "David Cantrell ", + "Graham Knop ", + "Ricardo Signes " + ] +} + diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..3fc7d11 --- /dev/null +++ b/META.yml @@ -0,0 +1,474 @@ +--- +abstract: 'An object representing a stack trace' +author: + - 'Dave Rolsky ' +build_requires: + ExtUtils::MakeMaker: '0' + File::Spec: '0' + IO::Handle: '0' + IPC::Open3: '0' + Test::More: '0.88' + base: '0' + bytes: '0' +configure_requires: + ExtUtils::MakeMaker: '0' +dynamic_config: 0 +generated_by: 'Dist::Zilla version 5.023, CPAN::Meta::Converter version 2.142690' +license: artistic_2 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: Devel-StackTrace +provides: + Devel::StackTrace: + file: lib/Devel/StackTrace.pm + version: '2.00' + Devel::StackTrace::Frame: + file: lib/Devel/StackTrace/Frame.pm + version: '2.00' +requires: + File::Spec: '0' + Scalar::Util: '0' + overload: '0' + perl: '5.006' + strict: '0' + warnings: '0' +resources: + bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Devel-StackTrace + homepage: http://metacpan.org/release/Devel-StackTrace + repository: git://github.com/autarch/Devel-StackTrace.git +version: '2.00' +x_Dist_Zilla: + perl: + version: '5.016003' + plugins: + - + class: Dist::Zilla::Plugin::MakeMaker + config: + Dist::Zilla::Role::TestRunner: + default_jobs: 1 + name: '@DROLSKY/MakeMaker' + version: '5.023' + - + class: Dist::Zilla::Plugin::Authority + name: '@DROLSKY/Authority' + version: '1.009' + - + class: Dist::Zilla::Plugin::AutoPrereqs + name: '@DROLSKY/AutoPrereqs' + version: '5.023' + - + class: Dist::Zilla::Plugin::GatherDir + config: + Dist::Zilla::Plugin::GatherDir: + exclude_filename: + - README.md + exclude_match: [] + follow_symlinks: '0' + include_dotfiles: '0' + prefix: '' + prune_directory: [] + root: . + name: '@DROLSKY/GatherDir' + version: '5.023' + - + class: Dist::Zilla::Plugin::GitHub::Meta + name: '@DROLSKY/GitHub::Meta' + version: '0.38' + - + class: Dist::Zilla::Plugin::GitHub::Update + name: '@DROLSKY/GitHub::Update' + version: '0.38' + - + class: Dist::Zilla::Plugin::MetaResources + name: '@DROLSKY/MetaResources' + version: '5.023' + - + class: Dist::Zilla::Plugin::MetaProvides::Package + config: + Dist::Zilla::Plugin::MetaProvides::Package: + finder_objects: + - + class: Dist::Zilla::Plugin::FinderCode + name: '@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM' + version: '5.023' + Dist::Zilla::Role::MetaProvider::Provider: + inherit_missing: '1' + inherit_version: '1' + meta_noindex: '1' + name: '@DROLSKY/MetaProvides::Package' + version: '2.001002' + - + class: Dist::Zilla::Plugin::NextRelease + name: '@DROLSKY/NextRelease' + version: '5.023' + - + class: Dist::Zilla::Plugin::PkgVersion + name: '@DROLSKY/PkgVersion' + version: '5.023' + - + class: Dist::Zilla::Plugin::Prereqs + config: + Dist::Zilla::Plugin::Prereqs: + phase: test + type: requires + name: '@DROLSKY/TestMoreDoneTesting' + version: '5.023' + - + class: Dist::Zilla::Plugin::PromptIfStale + config: + Dist::Zilla::Plugin::PromptIfStale: + check_all_plugins: '1' + check_all_prereqs: '1' + modules: [] + phase: release + skip: [] + name: '@DROLSKY/stale modules, release' + version: '0.028' + - + class: Dist::Zilla::Plugin::ReadmeAnyFromPod + name: '@DROLSKY/ReadmeMarkdownInBuild' + version: '0.142470' + - + class: Dist::Zilla::Plugin::ReadmeAnyFromPod + name: '@DROLSKY/ReadmeMarkdownInRoot' + version: '0.142470' + - + class: Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable + name: '@DROLSKY/Test::Pod::Coverage::Configurable' + version: '0.01' + - + class: Dist::Zilla::Plugin::Test::PodSpelling + name: '@DROLSKY/Test::PodSpelling' + version: '2.006008' + - + class: Dist::Zilla::Plugin::Test::ReportPrereqs + name: '@DROLSKY/Test::ReportPrereqs' + version: '0.019' + - + class: Dist::Zilla::Plugin::PruneCruft + name: '@DROLSKY/PruneCruft' + version: '5.023' + - + class: Dist::Zilla::Plugin::ManifestSkip + name: '@DROLSKY/ManifestSkip' + version: '5.023' + - + class: Dist::Zilla::Plugin::MetaYAML + name: '@DROLSKY/MetaYAML' + version: '5.023' + - + class: Dist::Zilla::Plugin::License + name: '@DROLSKY/License' + version: '5.023' + - + class: Dist::Zilla::Plugin::ExtraTests + name: '@DROLSKY/ExtraTests' + version: '5.023' + - + class: Dist::Zilla::Plugin::ExecDir + name: '@DROLSKY/ExecDir' + version: '5.023' + - + class: Dist::Zilla::Plugin::ShareDir + name: '@DROLSKY/ShareDir' + version: '5.023' + - + class: Dist::Zilla::Plugin::Manifest + name: '@DROLSKY/Manifest' + version: '5.023' + - + class: Dist::Zilla::Plugin::TestRelease + name: '@DROLSKY/TestRelease' + version: '5.023' + - + class: Dist::Zilla::Plugin::ConfirmRelease + name: '@DROLSKY/ConfirmRelease' + version: '5.023' + - + class: Dist::Zilla::Plugin::UploadToCPAN + name: '@DROLSKY/UploadToCPAN' + version: '5.023' + - + class: Dist::Zilla::Plugin::CheckPrereqsIndexed + name: '@DROLSKY/CheckPrereqsIndexed' + version: '0.012' + - + class: Dist::Zilla::Plugin::CopyReadmeFromBuild + name: '@DROLSKY/CopyReadmeFromBuild' + version: '0.0019' + - + class: Dist::Zilla::Plugin::DROLSKY::Contributors + name: '@DROLSKY/DROLSKY::Contributors' + version: '0.21' + - + class: Dist::Zilla::Plugin::DROLSKY::License + name: '@DROLSKY/DROLSKY::License' + version: '0.21' + - + class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch + config: + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::CheckFor::CorrectBranch' + version: '0.011' + - + class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts + config: + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::CheckFor::MergeConflicts' + version: '0.011' + - + class: Dist::Zilla::Plugin::Git::Contributors + config: + Dist::Zilla::Plugin::Git::Contributors: + include_authors: '0' + include_releaser: '1' + order_by: name + paths: [] + name: '@DROLSKY/Git::Contributors' + version: '0.008' + - + class: Dist::Zilla::Plugin::Git::Describe + name: '@DROLSKY/Git::Describe' + version: '0.003' + - + class: Dist::Zilla::Plugin::InstallGuide + name: '@DROLSKY/InstallGuide' + version: '1.200005' + - + class: Dist::Zilla::Plugin::Meta::Contributors + name: '@DROLSKY/Meta::Contributors' + version: '0.001' + - + class: Dist::Zilla::Plugin::MetaConfig + name: '@DROLSKY/MetaConfig' + version: '5.023' + - + class: Dist::Zilla::Plugin::MetaJSON + name: '@DROLSKY/MetaJSON' + version: '5.023' + - + class: Dist::Zilla::Plugin::SurgicalPodWeaver + config: + Dist::Zilla::Plugin::PodWeaver: + finder: + - ':InstallModules' + - ':ExecFiles' + plugins: + - + class: Pod::Weaver::Plugin::EnsurePod5 + name: '@CorePrep/EnsurePod5' + version: '4.006' + - + class: Pod::Weaver::Plugin::H1Nester + name: '@CorePrep/H1Nester' + version: '4.006' + - + class: Pod::Weaver::Section::Name + name: Name + version: '4.006' + - + class: Pod::Weaver::Section::Version + name: Version + version: '4.006' + - + class: Pod::Weaver::Section::Region + name: prelude + version: '4.006' + - + class: Pod::Weaver::Section::Generic + name: SYNOPSIS + version: '4.006' + - + class: Pod::Weaver::Section::Generic + name: DESCRIPTION + version: '4.006' + - + class: Pod::Weaver::Section::Leftovers + name: Leftovers + version: '4.006' + - + class: Pod::Weaver::Section::Region + name: postlude + version: '4.006' + - + class: Pod::Weaver::Section::Authors + name: Authors + version: '4.006' + - + class: Pod::Weaver::Section::Contributors + name: Contributors + version: '0.008' + - + class: Pod::Weaver::Section::Legal + name: Legal + version: '4.006' + name: '@DROLSKY/SurgicalPodWeaver' + version: '0.0021' + - + class: Dist::Zilla::Plugin::PodSyntaxTests + name: '@DROLSKY/PodSyntaxTests' + version: '5.023' + - + class: Dist::Zilla::Plugin::Test::CPAN::Changes + name: '@DROLSKY/Test::CPAN::Changes' + version: '0.008' + - + class: Dist::Zilla::Plugin::Test::Compile + config: + Dist::Zilla::Plugin::Test::Compile: + bail_out_on_fail: '0' + fail_on_warning: author + fake_home: '0' + filename: t/00-compile.t + module_finder: + - ':InstallModules' + needs_display: '0' + phase: test + script_finder: + - ':ExecFiles' + skips: [] + name: '@DROLSKY/Test::Compile' + version: '2.051' + - + class: Dist::Zilla::Plugin::Test::EOL + config: + Dist::Zilla::Plugin::Test::EOL: + filename: xt/author/eol.t + name: '@DROLSKY/Test::EOL' + version: '0.15' + - + class: Dist::Zilla::Plugin::Test::NoTabs + config: + Dist::Zilla::Plugin::Test::NoTabs: + filename: xt/author/no-tabs.t + finder: + - ':InstallModules' + - ':ExecFiles' + - ':TestFiles' + name: '@DROLSKY/Test::NoTabs' + version: '0.09' + - + class: Dist::Zilla::Plugin::Test::Pod::LinkCheck + name: '@DROLSKY/Test::Pod::LinkCheck' + version: '1.001' + - + class: Dist::Zilla::Plugin::Test::Pod::No404s + name: '@DROLSKY/Test::Pod::No404s' + version: '1.001' + - + class: Dist::Zilla::Plugin::Test::Portability + name: '@DROLSKY/Test::Portability' + version: '2.000005' + - + class: Dist::Zilla::Plugin::Test::Synopsis + name: '@DROLSKY/Test::Synopsis' + version: '2.000005' + - + class: Dist::Zilla::Plugin::Git::Check + config: + Dist::Zilla::Plugin::Git::Check: + untracked_files: die + Dist::Zilla::Role::Git::DirtyFiles: + allow_dirty: + - Changes + - CONTRIBUTING.md + - README.md + allow_dirty_match: [] + changelog: Changes + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::Check' + version: '2.025' + - + class: Dist::Zilla::Plugin::Git::Commit + config: + Dist::Zilla::Plugin::Git::Commit: + add_files_in: [] + commit_msg: v%v%n%n%c + time_zone: local + Dist::Zilla::Role::Git::DirtyFiles: + allow_dirty: + - Changes + - CONTRIBUTING.md + - README.md + allow_dirty_match: [] + changelog: Changes + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::Commit' + version: '2.025' + - + class: Dist::Zilla::Plugin::Git::Tag + config: + Dist::Zilla::Plugin::Git::Tag: + branch: ~ + signed: 0 + tag: v2.00 + tag_format: v%v + tag_message: v%v + time_zone: local + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::Tag' + version: '2.025' + - + class: Dist::Zilla::Plugin::Git::Push + config: + Dist::Zilla::Plugin::Git::Push: + push_to: + - origin + remotes_must_exist: 1 + Dist::Zilla::Role::Git::Repo: + repo_root: . + name: '@DROLSKY/Git::Push' + version: '2.025' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':InstallModules' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':IncModules' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':TestFiles' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':ExecFiles' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':ShareFiles' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':MainModule' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':AllFiles' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':NoFiles' + version: '5.023' + - + class: Dist::Zilla::Plugin::FinderCode + name: '@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM' + version: '5.023' + zilla: + class: Dist::Zilla::Dist::Builder + config: + is_trial: '0' + version: '5.023' +x_authority: cpan:DROLSKY +x_contributors: + - 'Dagfinn Ilmari Mannsåker ' + - 'David Cantrell ' + - 'Graham Knop ' + - 'Ricardo Signes ' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..b4912ac --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,73 @@ + +# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.023. +use strict; +use warnings; + +use 5.006; + +use ExtUtils::MakeMaker; + + + +my %WriteMakefileArgs = ( + "ABSTRACT" => "An object representing a stack trace", + "AUTHOR" => "Dave Rolsky ", + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => 0 + }, + "DISTNAME" => "Devel-StackTrace", + "EXE_FILES" => [], + "LICENSE" => "artistic_2", + "MIN_PERL_VERSION" => "5.006", + "NAME" => "Devel::StackTrace", + "PREREQ_PM" => { + "File::Spec" => 0, + "Scalar::Util" => 0, + "overload" => 0, + "strict" => 0, + "warnings" => 0 + }, + "TEST_REQUIRES" => { + "ExtUtils::MakeMaker" => 0, + "File::Spec" => 0, + "IO::Handle" => 0, + "IPC::Open3" => 0, + "Test::More" => "0.88", + "base" => 0, + "bytes" => 0 + }, + "VERSION" => "2.00", + "test" => { + "TESTS" => "t/*.t" + } +); + + +my %FallbackPrereqs = ( + "ExtUtils::MakeMaker" => 0, + "File::Spec" => 0, + "IO::Handle" => 0, + "IPC::Open3" => 0, + "Scalar::Util" => 0, + "Test::More" => "0.88", + "base" => 0, + "bytes" => 0, + "overload" => 0, + "strict" => 0, + "warnings" => 0 +); + + +unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { + delete $WriteMakefileArgs{TEST_REQUIRES}; + delete $WriteMakefileArgs{BUILD_REQUIRES}; + $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; +} + +delete $WriteMakefileArgs{CONFIGURE_REQUIRES} + unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; + +WriteMakefile(%WriteMakefileArgs); + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..73b3bf1 --- /dev/null +++ b/README.md @@ -0,0 +1,239 @@ +# NAME + +Devel::StackTrace - An object representing a stack trace + +# VERSION + +version 2.00 + +# SYNOPSIS + + use Devel::StackTrace; + + my $trace = Devel::StackTrace->new(); + + print $trace->as_string(); # like carp + + # from top (most recent) of stack to bottom. + while ( my $frame = $trace->next_frame() ) { + print "Has args\n" if $frame->hasargs(); + } + + # from bottom (least recent) of stack to top. + while ( my $frame = $trace->prev_frame() ) { + print "Sub: ", $frame->subroutine(), "\n"; + } + +# DESCRIPTION + +The `Devel::StackTrace` module contains two classes, C,Devel::StackTrace> and +[Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame). These objects encapsulate the information that +can retrieved via Perl's `caller()` function, as well as providing a simple +interface to this data. + +The `Devel::StackTrace` object contains a set of `Devel::StackTrace::Frame` +objects, one for each level of the stack. The frames contain all the data +available from `caller()`. + +This code was created to support my [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) class (part of +[Exception::Class](https://metacpan.org/pod/Exception::Class)) but may be useful in other contexts. + +# 'TOP' AND 'BOTTOM' OF THE STACK + +When describing the methods of the trace object, I use the words 'top' and +'bottom'. In this context, the 'top' frame on the stack is the most recent +frame and the 'bottom' is the least recent. + +Here's an example: + + foo(); # bottom frame is here + + sub foo { + bar(); + } + + sub bar { + Devel::StackTrace->new(); # top frame is here. + } + +# METHODS + +This class provide the following methods: + +## Devel::StackTrace->new(%named\_params) + +Returns a new Devel::StackTrace object. + +Takes the following parameters: + +- frame\_filter => $sub + + By default, Devel::StackTrace will include all stack frames before the + call to its constructor. + + However, you may want to filter out some frames with more granularity + than 'ignore\_package' or 'ignore\_class' allow. + + You can provide a subroutine which is called with the raw frame data + for each frame. This is a hash reference with two keys, "caller", and + "args", both of which are array references. The "caller" key is the + raw data as returned by Perl's `caller()` function, and the "args" + key are the subroutine arguments found in `@DB::args`. + + The filter should return true if the frame should be included, or + false if it should be skipped. + +- filter\_frames\_early => $boolean + + If this parameter is true, `frame_filter` will be called as soon as the + stacktrace is created, and before refs are stringified (if + `unsafe_ref_capture` is not set), rather than being filtered lazily when + [Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame) objects are first needed. + + This is useful if you want to filter based on the frame's arguments and want + to be able to examine object properties, for example. + +- ignore\_package => $package\_name OR \\@package\_names + + Any frames where the package is one of these packages will not be on + the stack. + +- ignore\_class => $package\_name OR \\@package\_names + + Any frames where the package is a subclass of one of these packages + (or is the same package) will not be on the stack. + + Devel::StackTrace internally adds itself to the 'ignore\_package' + parameter, meaning that the Devel::StackTrace package is **ALWAYS** + ignored. However, if you create a subclass of Devel::StackTrace it + will not be ignored. + +- skip\_frames => $integer + + This will cause this number of stack frames to be excluded from top of the + stack trace. This prevents the frames from being captured at all, and applies + before the `frame_filter`, `ignore_package`, or `ignore_class` options, + even with `filter_frames_early`. + +- unsafe\_ref\_capture => $boolean + + If this parameter is true, then Devel::StackTrace will store + references internally when generating stacktrace frames. + + **This option is very dangerous, and should never be used with exception + objects**. Using this option will keep any objects or references alive past + their normal lifetime, until the stack trace object goes out of scope. It can + keep objects alive even after their `DESTROY` sub is called, resulting it it + being called multiple times on the same object. + + If not set, Devel::StackTrace replaces any references with their stringified + representation. + +- no\_args => $boolean + + If this parameter is true, then Devel::StackTrace will not store caller + arguments in stack trace frames at all. + +- respect\_overload => $boolean + + By default, Devel::StackTrace will call `overload::AddrRef()` to get + the underlying string representation of an object, instead of + respecting the object's stringification overloading. If you would + prefer to see the overloaded representation of objects in stack + traces, then set this parameter to true. + +- max\_arg\_length => $integer + + By default, Devel::StackTrace will display the entire argument for each + subroutine call. Setting this parameter causes truncates each subroutine + argument's string representation if it is longer than this number of + characters. + +- message => $string + + By default, Devel::StackTrace will use 'Trace begun' as the message for the + first stack frame when you call `as_string`. You can supply an alternative + message using this option. + +- indent => $boolean + + If this parameter is true, each stack frame after the first will start with a + tab character, just like `Carp::confess()`. + +## $trace->next\_frame() + +Returns the next [Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame) object on the stack, going +down. If this method hasn't been called before it returns the first frame. It +returns `undef` when it reaches the bottom of the stack and then resets its +pointer so the next call to `$trace->next_frame()` or `$trace->prev_frame()` will work properly. + +## $trace->prev\_frame() + +Returns the next [Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame) object on the stack, going up. If +this method hasn't been called before it returns the last frame. It returns +undef when it reaches the top of the stack and then resets its pointer so the +next call to `$trace->next_frame()` or `$trace->prev_frame()` will +work properly. + +## $trace->reset\_pointer + +Resets the pointer so that the next call to `$trace->next_frame()` or `$trace->prev_frame()` will start at the top or bottom of the stack, as +appropriate. + +## $trace->frames() + +When this method is called with no arguments, it returns a list of +[Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame) objects. They are returned in order from top (most +recent) to bottom. + +This method can also be used to set the object's frames if you pass it a list +of [Devel::StackTrace::Frame](https://metacpan.org/pod/Devel::StackTrace::Frame) objects. + +This is useful if you want to filter the list of frames in ways that are more +complex than can be handled by the `$trace->filter_frames()` method: + + $stacktrace->frames( my_filter( $stacktrace->frames() ) ); + +## $trace->frame($index) + +Given an index, this method returns the relevant frame, or undef if there is +no frame at that index. The index is exactly like a Perl array. The first +frame is 0 and negative indexes are allowed. + +## $trace->frame\_count() + +Returns the number of frames in the trace object. + +## $trace->as\_string(\\%p) + +Calls `$frame->as_string()` on each frame from top to bottom, producing +output quite similar to the Carp module's cluck/confess methods. + +The optional `\%p` parameter only has one option. The `max_arg_length` +parameter truncates each subroutine argument's string representation if it is +longer than this number of characters. + +# SUPPORT + +Please submit bugs to the CPAN RT system at +http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel%3A%3AStackTrace +or via email at bug-devel-stacktrace@rt.cpan.org. + +# AUTHOR + +Dave Rolsky + +# CONTRIBUTORS + +- Dagfinn Ilmari Mannsåker +- David Cantrell +- Graham Knop +- Ricardo Signes + +# COPYRIGHT AND LICENSE + +This software is Copyright (c) 2000 - 2014 by David Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..f6873f0 --- /dev/null +++ b/dist.ini @@ -0,0 +1,13 @@ +name = Devel-StackTrace +author = Dave Rolsky +copyright_year = 2000 + +version = 2.00 + +[@DROLSKY] +dist = Devel-StackTrace +next_release_width = 6 +prereqs_skip = Test +stopwords = CPAN +stopwords = Rolsky +stopwords = stacktrace diff --git a/lib/Devel/StackTrace.pm b/lib/Devel/StackTrace.pm new file mode 100644 index 0000000..cf7ef43 --- /dev/null +++ b/lib/Devel/StackTrace.pm @@ -0,0 +1,531 @@ +package Devel::StackTrace; +# git description: v1.34-10-g810fd3f + +$Devel::StackTrace::VERSION = '2.00'; +use 5.006; + +use strict; +use warnings; + +use Devel::StackTrace::Frame; +use File::Spec; +use Scalar::Util qw( blessed ); + +use overload + '""' => \&as_string, + fallback => 1; + +sub new { + my $class = shift; + my %p = @_; + + $p{unsafe_ref_capture} = !delete $p{no_refs} + if exists $p{no_refs}; + + my $self = bless { + index => undef, + frames => [], + raw => [], + %p, + }, $class; + + $self->_record_caller_data(); + + return $self; +} + +sub _record_caller_data { + my $self = shift; + + my $filter = $self->{filter_frames_early} && $self->_make_frame_filter(); + + # We exclude this method by starting at least one frame back. + my $x = 1 + ( $self->{skip_frames} || 0 ); + + while ( + my @c + = $self->{no_args} + ? caller( $x++ ) + : do { + package # the newline keeps dzil from adding a version here + DB; + @DB::args = (); + caller( $x++ ); + } + ) { + + my @args; + + @args = $self->{no_args} ? () : @DB::args; + + my $raw = { + caller => \@c, + args => \@args, + }; + + next if $filter && !$filter->($raw); + + unless ( $self->{unsafe_ref_capture} ) { + $raw->{args} = [ map { ref $_ ? $self->_ref_to_string($_) : $_ } + @{ $raw->{args} } ]; + } + + push @{ $self->{raw} }, $raw; + } +} + +sub _ref_to_string { + my $self = shift; + my $ref = shift; + + return overload::AddrRef($ref) + if blessed $ref && $ref->isa('Exception::Class::Base'); + + return overload::AddrRef($ref) unless $self->{respect_overload}; + + local $@; + local $SIG{__DIE__}; + + my $str = eval { $ref . '' }; + + return $@ ? overload::AddrRef($ref) : $str; +} + +sub _make_frames { + my $self = shift; + + my $filter = !$self->{filter_frames_early} && $self->_make_frame_filter(); + + my $raw = delete $self->{raw}; + for my $r ( @{$raw} ) { + next if $filter && !$filter->($r); + + $self->_add_frame( $r->{caller}, $r->{args} ); + } +} + +my $default_filter = sub { 1 }; + +sub _make_frame_filter { + my $self = shift; + + my ( @i_pack_re, %i_class ); + if ( $self->{ignore_package} ) { + local $@; + local $SIG{__DIE__}; + + $self->{ignore_package} = [ $self->{ignore_package} ] + unless eval { @{ $self->{ignore_package} } }; + + @i_pack_re + = map { ref $_ ? $_ : qr/^\Q$_\E$/ } @{ $self->{ignore_package} }; + } + + my $p = __PACKAGE__; + push @i_pack_re, qr/^\Q$p\E$/; + + if ( $self->{ignore_class} ) { + $self->{ignore_class} = [ $self->{ignore_class} ] + unless ref $self->{ignore_class}; + %i_class = map { $_ => 1 } @{ $self->{ignore_class} }; + } + + my $user_filter = $self->{frame_filter}; + + return sub { + return 0 if grep { $_[0]{caller}[0] =~ /$_/ } @i_pack_re; + return 0 if grep { $_[0]{caller}[0]->isa($_) } keys %i_class; + + if ($user_filter) { + return $user_filter->( $_[0] ); + } + + return 1; + }; +} + +sub _add_frame { + my $self = shift; + my $c = shift; + my $p = shift; + + # eval and is_require are only returned when applicable under 5.00503. + push @$c, ( undef, undef ) if scalar @$c == 6; + + push @{ $self->{frames} }, + Devel::StackTrace::Frame->new( + $c, + $p, + $self->{respect_overload}, + $self->{max_arg_length}, + $self->{message}, + $self->{indent} + ); +} + +sub next_frame { + my $self = shift; + + # reset to top if necessary. + $self->{index} = -1 unless defined $self->{index}; + + my @f = $self->frames(); + if ( defined $f[ $self->{index} + 1 ] ) { + return $f[ ++$self->{index} ]; + } + else { + $self->{index} = undef; + return undef; + } +} + +sub prev_frame { + my $self = shift; + + my @f = $self->frames(); + + # reset to top if necessary. + $self->{index} = scalar @f unless defined $self->{index}; + + if ( defined $f[ $self->{index} - 1 ] && $self->{index} >= 1 ) { + return $f[ --$self->{index} ]; + } + else { + $self->{index} = undef; + return undef; + } +} + +sub reset_pointer { + my $self = shift; + + $self->{index} = undef; +} + +sub frames { + my $self = shift; + + if (@_) { + die + "Devel::StackTrace->frames() can only take Devel::StackTrace::Frame args\n" + if grep { !$_->isa('Devel::StackTrace::Frame') } @_; + + $self->{frames} = \@_; + } + else { + $self->_make_frames() if $self->{raw}; + } + + return @{ $self->{frames} }; +} + +sub frame { + my $self = shift; + my $i = shift; + + return unless defined $i; + + return ( $self->frames() )[$i]; +} + +sub frame_count { + my $self = shift; + + return scalar( $self->frames() ); +} + +sub as_string { + my $self = shift; + my $p = shift; + + my $st = ''; + my $first = 1; + foreach my $f ( $self->frames() ) { + $st .= $f->as_string( $first, $p ) . "\n"; + $first = 0; + } + + return $st; +} + +{ + package # hide from PAUSE + Devel::StackTraceFrame; + + our @ISA = 'Devel::StackTrace::Frame'; +} + +1; + +# ABSTRACT: An object representing a stack trace + +__END__ + +=pod + +=head1 NAME + +Devel::StackTrace - An object representing a stack trace + +=head1 VERSION + +version 2.00 + +=head1 SYNOPSIS + + use Devel::StackTrace; + + my $trace = Devel::StackTrace->new(); + + print $trace->as_string(); # like carp + + # from top (most recent) of stack to bottom. + while ( my $frame = $trace->next_frame() ) { + print "Has args\n" if $frame->hasargs(); + } + + # from bottom (least recent) of stack to top. + while ( my $frame = $trace->prev_frame() ) { + print "Sub: ", $frame->subroutine(), "\n"; + } + +=head1 DESCRIPTION + +The C module contains two classes, C,Devel::StackTrace> and +L. These objects encapsulate the information that +can retrieved via Perl's C function, as well as providing a simple +interface to this data. + +The C object contains a set of C +objects, one for each level of the stack. The frames contain all the data +available from C. + +This code was created to support my L class (part of +L) but may be useful in other contexts. + +=encoding UTF-8 + +=head1 'TOP' AND 'BOTTOM' OF THE STACK + +When describing the methods of the trace object, I use the words 'top' and +'bottom'. In this context, the 'top' frame on the stack is the most recent +frame and the 'bottom' is the least recent. + +Here's an example: + + foo(); # bottom frame is here + + sub foo { + bar(); + } + + sub bar { + Devel::StackTrace->new(); # top frame is here. + } + +=head1 METHODS + +This class provide the following methods: + +=head2 Devel::StackTrace->new(%named_params) + +Returns a new Devel::StackTrace object. + +Takes the following parameters: + +=over 4 + +=item * frame_filter => $sub + +By default, Devel::StackTrace will include all stack frames before the +call to its constructor. + +However, you may want to filter out some frames with more granularity +than 'ignore_package' or 'ignore_class' allow. + +You can provide a subroutine which is called with the raw frame data +for each frame. This is a hash reference with two keys, "caller", and +"args", both of which are array references. The "caller" key is the +raw data as returned by Perl's C function, and the "args" +key are the subroutine arguments found in C<@DB::args>. + +The filter should return true if the frame should be included, or +false if it should be skipped. + +=item * filter_frames_early => $boolean + +If this parameter is true, C will be called as soon as the +stacktrace is created, and before refs are stringified (if +C is not set), rather than being filtered lazily when +L objects are first needed. + +This is useful if you want to filter based on the frame's arguments and want +to be able to examine object properties, for example. + +=item * ignore_package => $package_name OR \@package_names + +Any frames where the package is one of these packages will not be on +the stack. + +=item * ignore_class => $package_name OR \@package_names + +Any frames where the package is a subclass of one of these packages +(or is the same package) will not be on the stack. + +Devel::StackTrace internally adds itself to the 'ignore_package' +parameter, meaning that the Devel::StackTrace package is B +ignored. However, if you create a subclass of Devel::StackTrace it +will not be ignored. + +=item * skip_frames => $integer + +This will cause this number of stack frames to be excluded from top of the +stack trace. This prevents the frames from being captured at all, and applies +before the C, C, or C options, +even with C. + +=item * unsafe_ref_capture => $boolean + +If this parameter is true, then Devel::StackTrace will store +references internally when generating stacktrace frames. + +B. Using this option will keep any objects or references alive past +their normal lifetime, until the stack trace object goes out of scope. It can +keep objects alive even after their C sub is called, resulting it it +being called multiple times on the same object. + +If not set, Devel::StackTrace replaces any references with their stringified +representation. + +=item * no_args => $boolean + +If this parameter is true, then Devel::StackTrace will not store caller +arguments in stack trace frames at all. + +=item * respect_overload => $boolean + +By default, Devel::StackTrace will call C to get +the underlying string representation of an object, instead of +respecting the object's stringification overloading. If you would +prefer to see the overloaded representation of objects in stack +traces, then set this parameter to true. + +=item * max_arg_length => $integer + +By default, Devel::StackTrace will display the entire argument for each +subroutine call. Setting this parameter causes truncates each subroutine +argument's string representation if it is longer than this number of +characters. + +=item * message => $string + +By default, Devel::StackTrace will use 'Trace begun' as the message for the +first stack frame when you call C. You can supply an alternative +message using this option. + +=item * indent => $boolean + +If this parameter is true, each stack frame after the first will start with a +tab character, just like C. + +=back + +=head2 $trace->next_frame() + +Returns the next L object on the stack, going +down. If this method hasn't been called before it returns the first frame. It +returns C when it reaches the bottom of the stack and then resets its +pointer so the next call to C<< $trace->next_frame() >> or C<< +$trace->prev_frame() >> will work properly. + +=head2 $trace->prev_frame() + +Returns the next L object on the stack, going up. If +this method hasn't been called before it returns the last frame. It returns +undef when it reaches the top of the stack and then resets its pointer so the +next call to C<< $trace->next_frame() >> or C<< $trace->prev_frame() >> will +work properly. + +=head2 $trace->reset_pointer + +Resets the pointer so that the next call to C<< $trace->next_frame() >> or C<< +$trace->prev_frame() >> will start at the top or bottom of the stack, as +appropriate. + +=head2 $trace->frames() + +When this method is called with no arguments, it returns a list of +L objects. They are returned in order from top (most +recent) to bottom. + +This method can also be used to set the object's frames if you pass it a list +of L objects. + +This is useful if you want to filter the list of frames in ways that are more +complex than can be handled by the C<< $trace->filter_frames() >> method: + + $stacktrace->frames( my_filter( $stacktrace->frames() ) ); + +=head2 $trace->frame($index) + +Given an index, this method returns the relevant frame, or undef if there is +no frame at that index. The index is exactly like a Perl array. The first +frame is 0 and negative indexes are allowed. + +=head2 $trace->frame_count() + +Returns the number of frames in the trace object. + +=head2 $trace->as_string(\%p) + +Calls C<< $frame->as_string() >> on each frame from top to bottom, producing +output quite similar to the Carp module's cluck/confess methods. + +The optional C<\%p> parameter only has one option. The C +parameter truncates each subroutine argument's string representation if it is +longer than this number of characters. + +=head1 SUPPORT + +Please submit bugs to the CPAN RT system at +http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel%3A%3AStackTrace +or via email at bug-devel-stacktrace@rt.cpan.org. + +=head1 AUTHOR + +Dave Rolsky + +=head1 CONTRIBUTORS + +=for stopwords Dagfinn Ilmari Mannsåker David Cantrell Graham Knop Ricardo Signes + +=over 4 + +=item * + +Dagfinn Ilmari Mannsåker + +=item * + +David Cantrell + +=item * + +Graham Knop + +=item * + +Ricardo Signes + +=back + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2000 - 2014 by David Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) + +=cut diff --git a/lib/Devel/StackTrace/Frame.pm b/lib/Devel/StackTrace/Frame.pm new file mode 100644 index 0000000..aad497a --- /dev/null +++ b/lib/Devel/StackTrace/Frame.pm @@ -0,0 +1,216 @@ +package Devel::StackTrace::Frame; +$Devel::StackTrace::Frame::VERSION = '2.00'; +use strict; +use warnings; + +# Create accessor routines +BEGIN { + no strict 'refs'; + foreach my $f ( + qw( package filename line subroutine hasargs + wantarray evaltext is_require hints bitmask args ) + ) { + next if $f eq 'args'; + *{$f} = sub { my $s = shift; return $s->{$f} }; + } +} + +{ + my @fields = ( + qw( package filename line subroutine hasargs wantarray + evaltext is_require hints bitmask ) + ); + + sub new { + my $proto = shift; + my $class = ref $proto || $proto; + + my $self = bless {}, $class; + + @{$self}{@fields} = @{ shift() }; + + # fixup unix-style paths on win32 + $self->{filename} = File::Spec->canonpath( $self->{filename} ); + + $self->{args} = shift; + + $self->{respect_overload} = shift; + + $self->{max_arg_length} = shift; + + $self->{message} = shift; + + $self->{indent} = shift; + + return $self; + } +} + +sub args { + my $self = shift; + + return @{ $self->{args} }; +} + +sub as_string { + my $self = shift; + my $first = shift; + my $p = shift; + + my $sub = $self->subroutine; + + # This code stolen straight from Carp.pm and then tweaked. All + # errors are probably my fault -dave + if ($first) { + $sub + = defined $self->{message} + ? $self->{message} + : 'Trace begun'; + } + else { + + # Build a string, $sub, which names the sub-routine called. + # This may also be "require ...", "eval '...' or "eval {...}" + if ( my $eval = $self->evaltext ) { + if ( $self->is_require ) { + $sub = "require $eval"; + } + else { + $eval =~ s/([\\\'])/\\$1/g; + $sub = "eval '$eval'"; + } + } + elsif ( $sub eq '(eval)' ) { + $sub = 'eval {...}'; + } + + # if there are any arguments in the sub-routine call, format + # them according to the format variables defined earlier in + # this file and join them onto the $sub sub-routine string + # + # We copy them because they're going to be modified. + # + if ( my @a = $self->args ) { + for (@a) { + + # set args to the string "undef" if undefined + $_ = "undef", next unless defined $_; + + # hack! + $_ = $self->Devel::StackTrace::_ref_to_string($_) + if ref $_; + + local $SIG{__DIE__}; + local $@; + + eval { + my $max_arg_length + = exists $p->{max_arg_length} + ? $p->{max_arg_length} + : $self->{max_arg_length}; + + if ( $max_arg_length + && length $_ > $max_arg_length ) { + substr( $_, $max_arg_length ) = '...'; + } + + s/'/\\'/g; + + # 'quote' arg unless it looks like a number + $_ = "'$_'" unless /^-?[\d.]+$/; + + # print control/high ASCII chars as 'M-' or '^' + s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg; + s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg; + }; + + if ( my $e = $@ ) { + $_ = $e =~ /malformed utf-8/i ? '(bad utf-8)' : '?'; + } + } + + # append ('all', 'the', 'arguments') to the $sub string + $sub .= '(' . join( ', ', @a ) . ')'; + $sub .= ' called'; + } + } + + # If the user opted into indentation (a la Carp::confess), pre-add a tab + my $tab = $self->{indent} && !$first ? "\t" : q{}; + + return "${tab}$sub at " . $self->filename . ' line ' . $self->line; +} + +1; + +# ABSTRACT: A single frame in a stack trace + +__END__ + +=pod + +=head1 NAME + +Devel::StackTrace::Frame - A single frame in a stack trace + +=head1 VERSION + +version 2.00 + +=head1 DESCRIPTION + +See L for details. + +=for Pod::Coverage new + +=head1 METHODS + +See Perl's C documentation for more information on what these +methods return. + +=head2 $frame->package() + +=head2 $frame->filename() + +=head2 $frame->line() + +=head2 $frame->subroutine() + +=head2 $frame->hasargs() + +=head2 $frame->wantarray() + +=head2 $frame->evaltext() + +Returns undef if the frame was not part of an eval. + +=head2 $frame->is_require() + +Returns undef if the frame was not part of a require. + +=head2 $frame->args() + +Returns the arguments passed to the frame. Note that any arguments that are +references are returned as references, not copies. + +=head2 $frame->hints() + +=head2 $frame->bitmask() + +=head2 $frame->as_string() + +Returns a string containing a description of the frame. + +=head1 AUTHOR + +Dave Rolsky + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2000 - 2014 by David Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) + +=cut diff --git a/t/00-compile.t b/t/00-compile.t new file mode 100644 index 0000000..b69d9cd --- /dev/null +++ b/t/00-compile.t @@ -0,0 +1,52 @@ +use 5.006; +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.051 + +use Test::More; + +plan tests => 2 + ($ENV{AUTHOR_TESTING} ? 1 : 0); + +my @module_files = ( + 'Devel/StackTrace.pm', + 'Devel/StackTrace/Frame.pm' +); + + + +# no fake home requested + +my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib'; + +use File::Spec; +use IPC::Open3; +use IO::Handle; + +open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; + +my @warnings; +for my $lib (@module_files) +{ + # see L + my $stderr = IO::Handle->new; + + my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]"); + binmode $stderr, ':crlf' if $^O eq 'MSWin32'; + my @_warnings = <$stderr>; + waitpid($pid, 0); + is($?, 0, "$lib loaded ok"); + + if (@_warnings) + { + warn @_warnings; + push @warnings, @_warnings; + } +} + + + +is(scalar(@warnings), 0, 'no warnings found') + or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING}; + + diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd new file mode 100644 index 0000000..4425a79 --- /dev/null +++ b/t/00-report-prereqs.dd @@ -0,0 +1,46 @@ +do { my $x = { + 'configure' => { + 'requires' => { + 'ExtUtils::MakeMaker' => '0' + } + }, + 'develop' => { + 'requires' => { + 'Pod::Coverage::TrustPod' => '0', + 'Test::CPAN::Changes' => '0.19', + 'Test::EOL' => '0', + 'Test::More' => '0.88', + 'Test::NoTabs' => '0', + 'Test::Pod' => '1.41', + 'Test::Pod::Coverage' => '1.08', + 'Test::Spelling' => '0.12', + 'Test::Synopsis' => '0' + } + }, + 'runtime' => { + 'requires' => { + 'File::Spec' => '0', + 'Scalar::Util' => '0', + 'overload' => '0', + 'perl' => '5.006', + 'strict' => '0', + 'warnings' => '0' + } + }, + 'test' => { + 'recommends' => { + 'CPAN::Meta' => '2.120900' + }, + 'requires' => { + 'ExtUtils::MakeMaker' => '0', + 'File::Spec' => '0', + 'IO::Handle' => '0', + 'IPC::Open3' => '0', + 'Test::More' => '0.88', + 'base' => '0', + 'bytes' => '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 index 0000000..402b3d9 --- /dev/null +++ b/t/00-report-prereqs.t @@ -0,0 +1,176 @@ +#!perl + +use strict; +use warnings; + +# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.019 + +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( + +); + +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'; +if ( $source && $HAS_CPAN_META ) { + if ( my $meta = eval { CPAN::Meta->load_file($source) } ) { + $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); + } +} +else { + $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 ); + 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 ( @dep_errors ) { + diag join("\n", + "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n", + "The following REQUIRED prerequisites were not satisfied:\n", + @dep_errors, + "\n" + ); +} + +pass; + +# vim: ts=4 sts=4 sw=4 et: diff --git a/t/01-basic.t b/t/01-basic.t new file mode 100644 index 0000000..4af30ee --- /dev/null +++ b/t/01-basic.t @@ -0,0 +1,573 @@ +use strict; +use warnings; + +use Test::More 0.88; + +use Devel::StackTrace; + +sub get_file_name { File::Spec->canonpath( ( caller(0) )[1] ) } +my $test_file_name = get_file_name(); + +# Test all accessors +{ + my $trace = foo(); + + my @f = (); + while ( my $f = $trace->prev_frame ) { push @f, $f; } + + my $cnt = scalar @f; + is( + $cnt, 4, + "Trace should have 4 frames" + ); + + @f = (); + while ( my $f = $trace->next_frame ) { push @f, $f; } + + $cnt = scalar @f; + is( + $cnt, 4, + "Trace should have 4 frames" + ); + + is( + $f[0]->package, 'main', + "First frame package should be main" + ); + + is( + $f[0]->filename, $test_file_name, + "First frame filename should be $test_file_name" + ); + + is( $f[0]->line, 1009, "First frame line should be 1009" ); + + is( + $f[0]->subroutine, 'Devel::StackTrace::new', + "First frame subroutine should be Devel::StackTrace::new" + ); + + is( $f[0]->hasargs, 1, "First frame hasargs should be true" ); + + ok( + !$f[0]->wantarray, + "First frame wantarray should be false" + ); + + my $trace_text = <<"EOF"; +Trace begun at $test_file_name line 1009 +main::baz(1, 2) called at $test_file_name line 1005 +main::bar(1) called at $test_file_name line 1001 +main::foo at $test_file_name line 13 +EOF + + is( $trace->as_string, $trace_text, 'trace text' ); +} + +# Test constructor params +{ + my $trace = SubTest::foo( ignore_class => 'Test' ); + + my @f = (); + while ( my $f = $trace->prev_frame ) { push @f, $f; } + + my $cnt = scalar @f; + + is( $cnt, 1, "Trace should have 1 frame" ); + + is( + $f[0]->package, 'main', + "The package for this frame should be main" + ); + + $trace = Test::foo( ignore_class => 'Test' ); + + @f = (); + while ( my $f = $trace->prev_frame ) { push @f, $f; } + + $cnt = scalar @f; + + is( $cnt, 1, "Trace should have 1 frame" ); + is( + $f[0]->package, 'main', + "The package for this frame should be main" + ); +} + +# 15 - stringification overloading +{ + my $trace = baz(); + + my $trace_text = <<"EOF"; +Trace begun at $test_file_name line 1009 +main::baz at $test_file_name line 99 +EOF + + my $t = "$trace"; + is( $t, $trace_text, 'trace text' ); +} + +# 16-18 - frame_count, frame, reset_pointer, frames methods +{ + my $trace = foo(); + + is( + $trace->frame_count, 4, + "Trace should have 4 frames" + ); + + my $f = $trace->frame(2); + + is( + $f->subroutine, 'main::bar', + "Frame 2's subroutine should be 'main::bar'" + ); + + $trace->next_frame; + $trace->next_frame; + $trace->reset_pointer; + + $f = $trace->next_frame; + is( + $f->subroutine, 'Devel::StackTrace::new', + "next_frame should return first frame after call to reset_pointer" + ); + + my @f = $trace->frames; + is( + scalar @f, 4, + "frames method should return four frames" + ); + + is( + $f[0]->subroutine, 'Devel::StackTrace::new', + "first frame's subroutine should be Devel::StackTrace::new" + ); + + is( + $f[3]->subroutine, 'main::foo', + "last frame's subroutine should be main::foo" + ); +} + +# Not storing references +{ + my $obj = RefTest->new; + + my $trace = $obj->{trace}; + + my $call_to_trace = ( $trace->frames )[1]; + + my @args = $call_to_trace->args; + + is( + scalar @args, 1, + "Only one argument should have been passed in the call to trace()" + ); + + like( + $args[0], qr/RefTest=HASH/, + "Actual object should be replaced by string 'RefTest=HASH'" + ); +} + +# Storing references +{ + my $obj = RefTest2->new; + + my $trace = $obj->{trace}; + + my $call_to_trace = ( $trace->frames )[1]; + + my @args = $call_to_trace->args; + + is( + scalar @args, 1, + "Only one argument should have been passed in the call to trace()" + ); + + isa_ok( $args[0], 'RefTest2' ); +} + +# Storing references (deprecated interface 1) +{ + my $obj = RefTestDep1->new; + + my $trace = $obj->{trace}; + + my $call_to_trace = ( $trace->frames )[1]; + + my @args = $call_to_trace->args; + + is( + scalar @args, 1, + "Only one argument should have been passed in the call to trace()" + ); + + isa_ok( $args[0], 'RefTestDep1' ); +} + +# No ref to Exception::Class::Base object without refs +if ( $Exception::Class::VERSION && $Exception::Class::VERSION >= 1.09 ) +{ + eval { + Exception::Class::Base->throw( + error => 'error', + show_trace => 1, + ); + }; + my $exc = $@; + eval { quux($exc) }; + + ok( !$@, 'create stacktrace with no refs and exception object on stack' ); +} + +{ + sub FooBar::some_sub { return Devel::StackTrace->new } + + my $trace = eval { FooBar::some_sub('args') }; + + my $f = ( $trace->frames )[2]; + + is( $f->subroutine, '(eval)', 'subroutine is (eval)' ); + + my @args = $f->args; + + is( scalar @args, 0, 'no args given to eval block' ); +} + +{ + { + package #hide + FooBarBaz; + + sub func2 { + return Devel::StackTrace->new( ignore_package => qr/^FooBar/ ); + } + sub func1 { FooBarBaz::func2() } + } + + my $trace = FooBarBaz::func1('args'); + + my @f = $trace->frames; + + is( scalar @f, 1, 'check regex as ignore_package arg' ); +} + +{ + package #hide + StringOverloaded; + + use overload '""' => sub { 'overloaded' }; +} + +{ + my $o = bless {}, 'StringOverloaded'; + + my $trace = baz($o); + + unlike( + $trace->as_string, qr/\boverloaded\b/, + 'overloading is ignored by default' + ); +} + +{ + my $o = bless {}, 'StringOverloaded'; + + my $trace = respect_overloading($o); + + like( + $trace->as_string, qr/\boverloaded\b/, + 'overloading is ignored by default' + ); +} + +{ + package #hide + BlowOnCan; + + sub can { die 'foo' } +} + +{ + my $o = bless {}, 'BlowOnCan'; + + my $trace = baz($o); + + like( + $trace->as_string, qr/BlowOnCan/, + 'death in overload::Overloaded is ignored' + ); +} + +{ + my $trace = max_arg_length('abcdefghijklmnop'); + + my $trace_text = <<"EOF"; +Trace begun at $test_file_name line 1021 +main::max_arg_length('abcdefghij...') called at $test_file_name line 305 +EOF + + is( $trace->as_string, $trace_text, 'trace text' ); + + my $trace_text_1 = <<"EOF"; +Trace begun at $test_file_name line 1021 +main::max_arg_length('abc...') called at $test_file_name line 305 +EOF + + is( + $trace->as_string( { max_arg_length => 3 } ), + $trace_text_1, + 'trace text, max_arg_length = 3', + ); +} + +SKIP: +{ + skip "Test only runs on Linux", 1 + unless $^O eq 'linux'; + + my $frame = Devel::StackTrace::Frame->new( + [ 'Foo', 'foo/bar///baz.pm', 10, 'bar', 1, 1, '', 0 ], + [] + ); + + is( $frame->filename, 'foo/bar/baz.pm', 'filename is canonicalized' ); +} + +{ + my $obj = RefTest4->new(); + + my $trace = $obj->{trace}; + + ok( + ( !grep { ref $_ } map { @{ $_->{args} } } @{ $trace->{raw} } ), + 'raw data does not contain any references when unsafe_ref_capture not set' + ); + + is( + $trace->{raw}[1]{args}[1], 'not a ref', + 'non-refs are preserved properly in raw data as well' + ); +} + +{ + my $trace = overload_no_stringify( CodeOverload->new() ); + + eval { $trace->as_string() }; + + is( + $@, q{}, + 'no error when respect_overload is true and object overloads but does not stringify' + ); +} + +{ + my $trace = Filter::foo(); + + my @frames = $trace->frames(); + is( scalar @frames, 2, 'frame_filtered trace has just 2 frames' ); + is( + $frames[0]->subroutine(), 'Devel::StackTrace::new', + 'first subroutine' + ); + is( + $frames[1]->subroutine(), 'Filter::bar', + 'second subroutine (skipped Filter::foo)' + ); +} + +{ + my $trace = FilterAllFrames::a_foo(); + + my @frames = $trace->frames(); + is( + scalar @frames, 2, + 'after filtering whole list of frames, got just 2 frames' + ); + is( + $frames[0]->subroutine(), 'FilterAllFrames::a_bar', + 'first subroutine' + ); + is( + $frames[1]->subroutine(), 'FilterAllFrames::a_foo', + 'second subroutine' + ); +} + +done_testing(); + +# This means I can move these lines down without constantly fiddling +# with the checks for line numbers in the tests. + +#line 1000 +sub foo { + bar( @_, 1 ); +} + +sub bar { + baz( @_, 2 ); +} + +sub baz { + Devel::StackTrace->new( @_ ? @_[ 0, 1 ] : () ); +} + +sub quux { + Devel::StackTrace->new(); +} + +sub respect_overloading { + Devel::StackTrace->new( respect_overload => 1 ); +} + +sub max_arg_length { + Devel::StackTrace->new( max_arg_length => 10 ); +} + +sub overload_no_stringify { + return Devel::StackTrace->new( respect_overload => 1 ); +} + +{ + package #hide + Test; + + sub foo { + trace(@_); + } + + sub trace { + Devel::StackTrace->new(@_); + } +} + +{ + package #hide + SubTest; + + use base qw(Test); + + sub foo { + trace(@_); + } + + sub trace { + Devel::StackTrace->new(@_); + } +} + +{ + package #hide + RefTest; + + sub new { + my $self = bless {}, shift; + + $self->{trace} = trace($self); + + return $self; + } + + sub trace { + Devel::StackTrace->new(); + } +} + +{ + package #hide + RefTest2; + + sub new { + my $self = bless {}, shift; + + $self->{trace} = trace($self); + + return $self; + } + + sub trace { + Devel::StackTrace->new( unsafe_ref_capture => 1 ); + } +} + +{ + package #hide + RefTestDep1; + + sub new { + my $self = bless {}, shift; + + $self->{trace} = trace($self); + + return $self; + } + + sub trace { + Devel::StackTrace->new( no_refs => 0 ); + } +} + +{ + package #hide + RefTest4; + + sub new { + my $self = bless {}, shift; + + $self->{trace} = trace( $self, 'not a ref' ); + + return $self; + } + + sub trace { + Devel::StackTrace->new(); + } +} + +{ + package #hide + CodeOverload; + + use overload '&{}' => sub { 'foo' }; + + sub new { + my $class = shift; + return bless {}, $class; + } +} + +{ + package #hide + Filter; + + sub foo { + bar(); + } + + sub bar { + return Devel::StackTrace->new( + frame_filter => sub { $_[0]{caller}[3] ne 'Filter::foo' } ); + } +} + +{ + package #hide + FilterAllFrames; + + sub a_foo { b_foo() } + sub b_foo { a_bar() } + sub a_bar { b_bar() } + + sub b_bar { + my $stacktrace = Devel::StackTrace->new(); + $stacktrace->frames( only_a_frames( $stacktrace->frames() ) ); + return $stacktrace; + } + + sub only_a_frames { + my @frames = @_; + return grep { $_->subroutine() =~ /^FilterAllFrames::a/ } @frames; + } +} diff --git a/t/02-bad-utf8.t b/t/02-bad-utf8.t new file mode 100644 index 0000000..0574553 --- /dev/null +++ b/t/02-bad-utf8.t @@ -0,0 +1,40 @@ +use strict; +use warnings; + +use Test::More; + +eval 'use Encode'; +plan skip_all => 'These tests require Encode.pm' + unless eval 'use Encode; 1'; + +plan skip_all => 'These tests require Perl 5.8.8+' + unless $] >= 5.008008; + +plan skip_all => 'These tests are not relevant with Perl 5.13.8+' + if $] >= 5.013008; + +use Devel::StackTrace; + +# This should be invalid UTF8 +my $raw_bad = do { use bytes; chr(0xED) . chr(0xA1) . chr(0xBA) }; + +my $decoded = Encode::decode( 'utf8' => $raw_bad ); +my $trace = foo($decoded); + +my $string = eval { $trace->as_string() }; + +my $e = $@; +is( + $e, '', + 'as_string() does not throw an exception' +); +like( + $string, qr/\Q(bad utf-8)/, + 'stringified output notes bad utf-8' +); + +sub foo { + Devel::StackTrace->new(); +} + +done_testing(); diff --git a/t/03-message.t b/t/03-message.t new file mode 100644 index 0000000..50eb219 --- /dev/null +++ b/t/03-message.t @@ -0,0 +1,34 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +sub foo { + return Devel::StackTrace->new(@_); +} + +sub make_dst { + foo(@_); +} + +{ + my $dst = make_dst(); + + like( + $dst->as_string(), qr/^Trace begun/, + q{default message is "Trace begun"} + ); +} + +{ + my $dst = make_dst( message => 'Foo bar' ); + + like( + $dst->as_string(), qr/^Foo bar/, + q{set explicit message for trace} + ); +} + +done_testing(); diff --git a/t/04-indent.t b/t/04-indent.t new file mode 100644 index 0000000..5b0391c --- /dev/null +++ b/t/04-indent.t @@ -0,0 +1,35 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +sub foo { + return Devel::StackTrace->new(@_); +} + +sub make_dst { + foo(@_); +} + +{ + my $dst = make_dst(); + + for my $line ( split /\n/, $dst->as_string() ) { + unlike( $line, qr/^\s/, 'line does not start with whitespace' ); + } +} + +{ + my $dst = make_dst( indent => 1 ); + + my @lines = split /\n/, $dst->as_string(); + shift @lines; + + for my $line (@lines) { + like( $line, qr/^\s/, 'line starts with whitespace' ); + } +} + +done_testing(); diff --git a/t/05-back-compat.t b/t/05-back-compat.t new file mode 100644 index 0000000..3b0c4fc --- /dev/null +++ b/t/05-back-compat.t @@ -0,0 +1,10 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +isa_ok( 'Devel::StackTraceFrame', 'Devel::StackTrace::Frame' ); + +done_testing(); diff --git a/t/06-dollar-at.t b/t/06-dollar-at.t new file mode 100644 index 0000000..fdf061a --- /dev/null +++ b/t/06-dollar-at.t @@ -0,0 +1,24 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +{ + $@ = my $msg = q{Don't tread on me}; + + Devel::StackTrace->new()->frame(0)->as_string(); + + is( $@, $msg, '$@ is not overwritten in as_string() method' ); +} + +{ + $@ = my $msg = q{Don't tread on me}; + + Devel::StackTrace->new( ignore_package => 'Foo' )->frames(); + + is( $@, $msg, '$@ is not overwritten in _make_filter() method' ); +} + +done_testing(); diff --git a/t/07-no-args.t b/t/07-no-args.t new file mode 100644 index 0000000..7bdc166 --- /dev/null +++ b/t/07-no-args.t @@ -0,0 +1,45 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +{ + my $trace = foo( 1, 2 ); + is_deeply( + [ map { [ $_->args() ] } $trace->frames() ], + [ + ['Devel::StackTrace'], + [ 3, 4 ], + [ 1, 2 ], + ], + 'trace includes args' + ); + + $trace = foo( 0, 2 ); + is_deeply( + [ map { [ $_->args() ] } $trace->frames() ], + [ + [], + [], + [], + ], + 'trace does not include args' + ); + +} + +done_testing(); + +sub foo { + $_[0] ? bar( 3, 4 ) : baz( 3, 4 ); +} + +sub bar { + return Devel::StackTrace->new(); +} + +sub baz { + return Devel::StackTrace->new( no_args => 1 ); +} diff --git a/t/08-filter-early.t b/t/08-filter-early.t new file mode 100644 index 0000000..3471a4d --- /dev/null +++ b/t/08-filter-early.t @@ -0,0 +1,31 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +{ + my $trace = foo( [] ); + is( + 0 + grep( ref, map { $_->args } $trace->frames ), 0, + 'args stringified in trace' + ); +} + +done_testing(); + +sub foo { + return Devel::StackTrace->new( + frame_filter => sub { + my $frame = shift; + if ( $frame->{caller}[3] eq "main::foo" ) { + ok( ref $frame->{args}[0], 'ref arg passed to filter' ); + } + 1; + }, + filter_frames_early => 1, + no_refs => 1, + ); +} + diff --git a/t/09-skip-frames.t b/t/09-skip-frames.t new file mode 100644 index 0000000..3834e68 --- /dev/null +++ b/t/09-skip-frames.t @@ -0,0 +1,41 @@ +use strict; +use warnings; + +use Test::More; + +use Devel::StackTrace; + +{ + my $trace = baz(); + my @f; + while ( my $f = $trace->next_frame ) { push @f, $f; } + + my $cnt = scalar @f; + is( + $cnt, 2, + "Trace should have 2 frames" + ); + + is( + $f[0]->subroutine, 'main::bar', + "First frame subroutine should be main::bar" + ); + is( + $f[1]->subroutine, 'main::baz', + "First frame subroutine should be main::baz" + ); +} + +done_testing(); + +sub foo { + return Devel::StackTrace->new( skip_frames => 2 ); +} + +sub bar { + foo(); +} + +sub baz { + bar(); +} diff --git a/t/author-eol.t b/t/author-eol.t new file mode 100644 index 0000000..2348cd2 --- /dev/null +++ b/t/author-eol.t @@ -0,0 +1,16 @@ + +BEGIN { + unless ($ENV{AUTHOR_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for testing by the author'); + } +} + +use strict; +use warnings; +use Test::More; + +# generated by Dist::Zilla::Plugin::Test::EOL 0.15 +use Test::EOL; + +all_perl_files_ok({ trailing_whitespace => 1 }); diff --git a/t/author-no-tabs.t b/t/author-no-tabs.t new file mode 100644 index 0000000..3a99740 --- /dev/null +++ b/t/author-no-tabs.t @@ -0,0 +1,45 @@ + +BEGIN { + unless ($ENV{AUTHOR_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for testing by the author'); + } +} + +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.09 + +use Test::More 0.88; +use Test::NoTabs; + +my @files = ( + 'lib/Devel/StackTrace.pm', + 'lib/Devel/StackTrace/Frame.pm', + 't/00-compile.t', + 't/00-report-prereqs.dd', + 't/00-report-prereqs.t', + 't/01-basic.t', + 't/02-bad-utf8.t', + 't/03-message.t', + 't/04-indent.t', + 't/05-back-compat.t', + 't/06-dollar-at.t', + 't/07-no-args.t', + 't/08-filter-early.t', + 't/09-skip-frames.t', + 't/author-eol.t', + 't/author-no-tabs.t', + 't/author-pod-spell.t', + 't/release-cpan-changes.t', + 't/release-pod-coverage.t', + 't/release-pod-linkcheck.t', + 't/release-pod-no404s.t', + 't/release-pod-syntax.t', + 't/release-portability.t', + 't/release-synopsis.t' +); + +notabs_ok($_) foreach @files; +done_testing; diff --git a/t/author-pod-spell.t b/t/author-pod-spell.t new file mode 100644 index 0000000..e687980 --- /dev/null +++ b/t/author-pod-spell.t @@ -0,0 +1,45 @@ + +BEGIN { + unless ($ENV{AUTHOR_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for testing by the author'); + } +} + +use strict; +use warnings; +use Test::More; + +# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.006008 +use Test::Spelling 0.12; +use Pod::Wordlist; + + +add_stopwords(); +all_pod_files_spelling_ok( qw( bin lib ) ); +__DATA__ +DROLSKY +DROLSKY's +Rolsky +Rolsky's +CPAN +stacktrace +Dave +autarch +David +Dagfinn +Ilmari +Mannsåker +ilmari +Cantrell +david +Graham +Knop +haarg +Ricardo +Signes +rjbs +lib +Devel +StackTrace +Frame diff --git a/t/release-cpan-changes.t b/t/release-cpan-changes.t new file mode 100644 index 0000000..214650f --- /dev/null +++ b/t/release-cpan-changes.t @@ -0,0 +1,19 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + + +use strict; +use warnings; + +use Test::More 0.96 tests => 2; +use_ok('Test::CPAN::Changes'); +subtest 'changes_ok' => sub { + changes_file_ok('Changes'); +}; +done_testing(); diff --git a/t/release-pod-coverage.t b/t/release-pod-coverage.t new file mode 100644 index 0000000..1f3b7b6 --- /dev/null +++ b/t/release-pod-coverage.t @@ -0,0 +1,43 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + +# This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable. + +use Test::Pod::Coverage 1.08; +use Test::More 0.88; +use Pod::Coverage::TrustPod; + +my %skip = map { $_ => 1 } qw( ); + +my @modules; +for my $module ( all_modules() ) { + next if $skip{$module}; + + push @modules, $module; +} + +plan skip_all => 'All the modules we found were excluded from POD coverage test.' + unless @modules; + +plan tests => scalar @modules; + +my %trustme = (); + +for my $module ( sort @modules ) { + pod_coverage_ok( + $module, + { + coverage_class => 'Pod::Coverage::TrustPod', + trustme => $trustme{$module} || [], + }, + "pod coverage for $module" + ); +} + +done_testing(); diff --git a/t/release-pod-linkcheck.t b/t/release-pod-linkcheck.t new file mode 100644 index 0000000..654cf06 --- /dev/null +++ b/t/release-pod-linkcheck.t @@ -0,0 +1,28 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + + +use strict; +use warnings; +use Test::More; + +foreach my $env_skip ( qw( + SKIP_POD_LINKCHECK +) ){ + plan skip_all => "\$ENV{$env_skip} is set, skipping" + if $ENV{$env_skip}; +} + +eval "use Test::Pod::LinkCheck"; +if ( $@ ) { + plan skip_all => 'Test::Pod::LinkCheck required for testing POD'; +} +else { + Test::Pod::LinkCheck->new->all_pod_ok; +} diff --git a/t/release-pod-no404s.t b/t/release-pod-no404s.t new file mode 100644 index 0000000..da185ec --- /dev/null +++ b/t/release-pod-no404s.t @@ -0,0 +1,29 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + + +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/t/release-pod-syntax.t b/t/release-pod-syntax.t new file mode 100644 index 0000000..cdd6a6c --- /dev/null +++ b/t/release-pod-syntax.t @@ -0,0 +1,14 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + +# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. +use Test::More; +use Test::Pod 1.41; + +all_pod_files_ok(); diff --git a/t/release-portability.t b/t/release-portability.t new file mode 100644 index 0000000..f0fd79f --- /dev/null +++ b/t/release-portability.t @@ -0,0 +1,19 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + + +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/t/release-synopsis.t b/t/release-synopsis.t new file mode 100644 index 0000000..2d9b8ee --- /dev/null +++ b/t/release-synopsis.t @@ -0,0 +1,13 @@ +#!perl + +BEGIN { + unless ($ENV{RELEASE_TESTING}) { + require Test::More; + Test::More::plan(skip_all => 'these tests are for release candidate testing'); + } +} + + +use Test::Synopsis; + +all_synopsis_ok(); diff --git a/weaver.ini b/weaver.ini new file mode 100644 index 0000000..90c76a6 --- /dev/null +++ b/weaver.ini @@ -0,0 +1,17 @@ +[@CorePrep] + +[Name] +[Version] + +[Region / prelude] + +[Generic / SYNOPSIS] +[Generic / DESCRIPTION] + +[Leftovers] + +[Region / postlude] + +[Authors] +[Contributors] +[Legal] -- cgit v1.2.1