diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2014-11-01 18:05:53 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2014-11-01 18:05:53 +0000 |
commit | 22d921a6e9c4fc98cda05e898a8137c7e8dae970 (patch) | |
tree | 02f6ebea03827fad3bde7a7418593b3cdd13a46a | |
download | Devel-StackTrace-tarball-Devel-StackTrace-2.00.tar.gz |
Devel-StackTrace-2.00HEADDevel-StackTrace-2.00master
-rw-r--r-- | Changes | 336 | ||||
-rw-r--r-- | INSTALL | 43 | ||||
-rw-r--r-- | LICENSE | 207 | ||||
-rw-r--r-- | MANIFEST | 35 | ||||
-rw-r--r-- | META.json | 650 | ||||
-rw-r--r-- | META.yml | 474 | ||||
-rw-r--r-- | Makefile.PL | 73 | ||||
-rw-r--r-- | README.md | 239 | ||||
-rw-r--r-- | dist.ini | 13 | ||||
-rw-r--r-- | lib/Devel/StackTrace.pm | 531 | ||||
-rw-r--r-- | lib/Devel/StackTrace/Frame.pm | 216 | ||||
-rw-r--r-- | t/00-compile.t | 52 | ||||
-rw-r--r-- | t/00-report-prereqs.dd | 46 | ||||
-rw-r--r-- | t/00-report-prereqs.t | 176 | ||||
-rw-r--r-- | t/01-basic.t | 573 | ||||
-rw-r--r-- | t/02-bad-utf8.t | 40 | ||||
-rw-r--r-- | t/03-message.t | 34 | ||||
-rw-r--r-- | t/04-indent.t | 35 | ||||
-rw-r--r-- | t/05-back-compat.t | 10 | ||||
-rw-r--r-- | t/06-dollar-at.t | 24 | ||||
-rw-r--r-- | t/07-no-args.t | 45 | ||||
-rw-r--r-- | t/08-filter-early.t | 31 | ||||
-rw-r--r-- | t/09-skip-frames.t | 41 | ||||
-rw-r--r-- | t/author-eol.t | 16 | ||||
-rw-r--r-- | t/author-no-tabs.t | 45 | ||||
-rw-r--r-- | t/author-pod-spell.t | 45 | ||||
-rw-r--r-- | t/release-cpan-changes.t | 19 | ||||
-rw-r--r-- | t/release-pod-coverage.t | 43 | ||||
-rw-r--r-- | t/release-pod-linkcheck.t | 28 | ||||
-rw-r--r-- | t/release-pod-no404s.t | 29 | ||||
-rw-r--r-- | t/release-pod-syntax.t | 14 | ||||
-rw-r--r-- | t/release-portability.t | 19 | ||||
-rw-r--r-- | t/release-synopsis.t | 13 | ||||
-rw-r--r-- | weaver.ini | 17 |
34 files changed, 4212 insertions, 0 deletions
@@ -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) @@ -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 @@ -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 <autarch@urth.org>" + ], + "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 <ilmari@ilmari.org>", + "David Cantrell <david@cantrell.org.uk>", + "Graham Knop <haarg@haarg.org>", + "Ricardo Signes <rjbs@cpan.org>" + ] +} + 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 <autarch@urth.org>' +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 <ilmari@ilmari.org>' + - 'David Cantrell <david@cantrell.org.uk>' + - 'Graham Knop <haarg@haarg.org>' + - 'Ricardo Signes <rjbs@cpan.org>' 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 <autarch\@urth.org>", + "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 <autarch@urth.org> + +# CONTRIBUTORS + +- Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> +- David Cantrell <david@cantrell.org.uk> +- Graham Knop <haarg@haarg.org> +- Ricardo Signes <rjbs@cpan.org> + +# 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 <autarch@urth.org> +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<Devel::StackTrace> module contains two classes, C,Devel::StackTrace> and +L<Devel::StackTrace::Frame>. These objects encapsulate the information that +can retrieved via Perl's C<caller()> function, as well as providing a simple +interface to this data. + +The C<Devel::StackTrace> object contains a set of C<Devel::StackTrace::Frame> +objects, one for each level of the stack. The frames contain all the data +available from C<caller()>. + +This code was created to support my L<Exception::Class::Base> class (part of +L<Exception::Class>) 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<caller()> 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<frame_filter> will be called as soon as the +stacktrace is created, and before refs are stringified (if +C<unsafe_ref_capture> is not set), rather than being filtered lazily when +L<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. + +=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<ALWAYS> +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<frame_filter>, C<ignore_package>, or C<ignore_class> options, +even with C<filter_frames_early>. + +=item * unsafe_ref_capture => $boolean + +If this parameter is true, then Devel::StackTrace will store +references internally when generating stacktrace frames. + +B<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 C<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. + +=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<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. + +=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<as_string>. 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<Carp::confess()>. + +=back + +=head2 $trace->next_frame() + +Returns the next L<Devel::StackTrace::Frame> object on the stack, going +down. If this method hasn't been called before it returns the first frame. It +returns C<undef> 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<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 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<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 L<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 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<max_arg_length> +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 <autarch@urth.org> + +=head1 CONTRIBUTORS + +=for stopwords Dagfinn Ilmari Mannsåker David Cantrell Graham Knop Ricardo Signes + +=over 4 + +=item * + +Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> + +=item * + +David Cantrell <david@cantrell.org.uk> + +=item * + +Graham Knop <haarg@haarg.org> + +=item * + +Ricardo Signes <rjbs@cpan.org> + +=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-<char>' or '^<char>' + 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<Devel::StackTrace> for details. + +=for Pod::Coverage new + +=head1 METHODS + +See Perl's C<caller()> 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 <autarch@urth.org> + +=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<perlfaq8/How can I capture STDERR from an external command?> + 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(<DATA>); +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] |