From 485bc7f989e83d5a6b74c62442f9bb7cf9ab24d3 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 16 2020 11:56:01 +0000 Subject: perl-Exception-Class-1.44 base --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..26801ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,131 @@ +# CONTRIBUTING + +Thank you for considering contributing to this distribution. This file +contains instructions that will help you work with the source code. + +Please note that if you have any questions or difficulties, you can reach the +maintainer(s) through the bug queue described later in this document +(preferred), or by emailing the releaser directly. You are not required to +follow any of the steps in this document to submit a patch or bug report; +these are just recommendations, intended to help you (and help us help you +faster). + + +The distribution is managed with +[Dist::Zilla](https://metacpan.org/release/Dist-Zilla). + +However, you can still compile and test the code with the `Makefile.PL` or +`Build.PL` in the repository: + + perl Makefile.PL + make + make test + +or + perl Build.PL + ./Build + ./Build test + +As well as: + + $ prove -bvr t + +or + + $ perl -Mblib t/some_test_file.t + +You may need to satisfy some dependencies. The easiest way to satisfy +dependencies is to install the last release. This is available at +https://metacpan.org/release/Exception-Class + +If you use cpanminus, you can do it without downloading the tarball first: + + $ cpanm --reinstall --installdeps --with-recommends Exception::Class + +Dist::Zilla is a very powerful authoring tool, but requires a number of +author-specific plugins. If you would like to use it for contributing, install +it from CPAN, then run one of the following commands, depending on your CPAN +client: + + $ cpan `dzil authordeps --missing` + +or + + $ dzil authordeps --missing | cpanm + +They may also be additional requirements not needed by the dzil build which +are needed for tests or other development: + + $ cpan `dzil listdeps --author --missing` + +or + + $ dzil listdeps --author --missing | cpanm + +Or, you can use the 'dzil stale' command to install all requirements at once: + + $ cpan Dist::Zilla::App::Command::stale + $ cpan `dzil stale --all` + +or + + $ cpanm Dist::Zilla::App::Command::stale + $ dzil stale --all | cpanm + +You can also do this via cpanm directly: + + $ cpanm --reinstall --installdeps --with-develop --with-recommends Exception::Class + +Once installed, here are some dzil commands you might try: + + $ dzil build + $ dzil test + $ dzil test --release + $ dzil xtest + $ dzil listdeps --json + $ dzil build --notgz + +You can learn more about Dist::Zilla at http://dzil.org/. + +The code for this distribution is [hosted at GitHub](https://github.com/houseabsolute/Exception-Class). + +You can submit code changes by forking the repository, pushing your code +changes to your clone, and then submitting a pull request. Detailed +instructions for doing that is available here: + +https://help.github.com/articles/creating-a-pull-request + +If you have found a bug, but do not have an accompanying patch to fix it, you +can submit an issue report [via the web](https://github.com/houseabsolute/Exception-Class/issues) +). +This is a good place to send your questions about the usage of this distribution. + +## Travis + +All pull requests for this distribution will be automatically tested by +[Travis](https://travis-ci.org/) and the build status will be reported on the +pull request page. If your build fails, please take a look at the output. + +## TidyAll + +This distribution uses +[Code::TidyAll](https://metacpan.org/release/Code-TidyAll) to enforce a +uniform coding style. This is tested as part of the author testing suite. You +can install and run tidyall by running the following commands: + + $ cpanm Code::TidyAll + $ tidyall -a + +Please run this before committing your changes and address any issues it +brings up. + +## Contributor Names + +If you send a patch or pull request, your name and email address will be +included in the documentation as a contributor (using the attribution on the +commit or patch), unless you specifically request for it not to be. If you +wish to be listed under a different name or address, you should submit a pull +request to the .mailmap file to contain the correct mapping. + +This file was generated via Dist::Zilla::Plugin::GenerateFile::FromShareDir 0.013 from a +template file originating in Dist-Zilla-PluginBundle-DROLSKY-0.88. diff --git a/Changes b/Changes new file mode 100644 index 0000000..7697a4e --- /dev/null +++ b/Changes @@ -0,0 +1,446 @@ +1.44 2017-12-10 + +- You can now pass "frame_filter", "filter_frames_early", and "skip_frames" to + the throw() method of an exception class. These will be passed on to the + Devel::StackTrace constructor. Requested by Frédéric Brière. GH #6. + + +1.43 2017-07-09 + +- The full_message() method in Exception::Class::Base now calls message() + instead of accessing the object's hash key. This makes it easier to override + message() in a subclass. Patch by Alexander Batyrshin. PR #11. + + +1.42 2017-01-04 + +- Generated exception classes are now added to %INC. If you subclass a + generated class with "use base" then base.pm will no longer attempt to load + the requested class. Patch by Todd Rinaldo. PR #8. + + +1.41 2016-12-07 + +- Switch to GitHub Issues. + + +1.40 2016-01-29 + +- Fixed broken metadata. Reported by Slaven Rezić. GitHub #3. + + +1.39 2014-11-01 + +- Replaced the Exception::Class::Base->NoRefs method with UnsafeRefCapture to + match changes in Devel::StackTrace 2.00. The old method is deprecated but + will continue to work. + + +1.38 2014-05-05 + +- An exception without a message will now default to either the associated + exception class description or the string "[Generic exception]". Patch by + Ricardo Signes. PR #2. + +- Added field_hash() and context_hash() methods. Patch by Ricardo Signes. PR + #1. + + +1.37 2013-02-24 + +- I now recommend you use Throwable instead of this module. It has a nicer, + more modern interface. + +- Fixed various bugs and confusion in the docs. + + +1.36 2012-11-17 + +- Fixed some stupidity in the tests that appears to have been highlighted by + recent changes to Devel::StackTrace. Reported by Dan Horne. RT #81245. + + +1.35 2012-09-17 + +- Require Class::Data::Inheritable 0.02+. + + +1.34 2012-09-16 + +- 1.33 did not declare any prereqs. Oops. Reported by Roham McGovern. RT + #79677. + + +1.33 2012-09-16 + +- Fixed warning from basic.t on 5.17.x. RT #79121. + + +1.32 2010-06-28 + +- Removed Test::Most dependency, which was causing a circular dependency + chain. Reported by Burak Gursoy. RT #58889. + + +1.31 2010-06-26 + +- Made it possible to use Exception::Class::Base without loading + Exception::Class. Reported by Alex Peters. RT #57269. + + +1.30 2010-03-20 + +- Added the ability to create light weight exceptions, which don't record a + stack trace or any other context info (time, pid, etc.). This is done by + setting $class->NoContextInfo to a true value for the exception class. Based + on a patch by Graham Barr. RT #54826. + + +1.29 2009-05-08 + +- Make sure that there is only one line that the toolchain can pick up + when looking for this module's $VERSION. + + +1.28 2009-05-06 + +* Removed Exception::Class::Base->do_trace and ->NoObjectRefs, both of + which have been undocumented for many years. + +- Moved Exception::Class::Base to its own file, so it doesn't + overwrite the $VERSION in Exception::Class (and for general + sanity). Reported by Kirk Baucom. + + +1.27 2009-05-04 + +* The error message for an exception no longer defaults to $!. This + was a bad idea, and has bitten some people. Addresses RT #43600. + +* I'm now only supporting Perl 5.8.1+. This module broke on 5.6.2 (and + 5.8.0) at some point and I don't really care enough to fix + it. Non-intrusive patches to make it work on 5.8.0 or less (again) + will be accepted. + + +1.26 2008-10-25 + +- No code changes, just bumped the Devel::StackTrace dependency to + 1.20. + + The changes in 1.25 exposed a bug in Devel::StackTrace which has now + been fixed. This caused HTML::Mason test failures, among other + things. Reported by Andreas Koenig. + + +1.25 2008-10-21 + +- The Exception::Class::Base constructor no longer looks at the + associated Devel::StackTrace object's frames. Avoiding looking at + the frames should make constructing exception objects much + faster. Instead the information based on the stack trace frames - + package(), file(), and line() - is only retrieved when it is + requested. Patch by Ruslan Zakirov. RT #40221. + + +1.24 2008-03-30 + +- Added a MaxArgLength class parameter to go along with the new + max_arg_length constructor param for Devel::StackTrace + objects. Patch by Ian Burrell. + + +1.23 2006-01-14 + +- Fix code bugs in the SYNOPSIS. + +- Exception::Class->caught() now returns $@ if not given an argument. + + +1.22 2005-09-30 + +- Added Exception::Class::Base->caught() so you can write + "My::Error->caught()". Apparently this is what Damian documented in + Perl Best Practices. Thanks to JD Hedden for pointing this out. + +- Require Devel::StackTrace 1.12 to fix a test failure on Win32. + Reported by Garrett Goebel. + + +1.21 2005-04-17 + +- Added Exception::Class->caught() as syntactic sugar for catching + exceptions in a "safe" manner. See the docs for details. Suggested + by Damian Conway. + + +1.20 2005-01-01 + +- Moved the Classes() method to Exception::Class, now as a function, + which is where it really belongs. Also corrected the docs for this + function, as they incorrectly implied that it would return all + subclasses, instead of just subclasses which were created when + loading Exception::Class. + +- Added a Build.PL file, and releases are now signed with + Module::Signature. + +- Added ignore_class and ignore_package constructor parameters. Patch + by Daisuke Maki. + + +1.19 2004-03-10 + +- Identical to 1.18 except that it requires Devel::StackTrace 1.10, + which fixes a failure in this package's test when run with Perl + 5.6.1. Reported by Jesse Erlbaum. + + +1.18 2004-02-21 + +- Added RespectOverload class method, which can be used to control the + respect_overload parameter for Devel::StackTrace objects. + + +1.17 2004-02-20 + +- Document that new() is always called to create an + Exception::Class::Base object. Suggested by Steve Hay. + +- Fix a test that failed with Devel::StackTrace 1.05. Basically, the + test expected output from Devel::StackTrace that reflected a bug + that was fixed in 1.05. Reported by Jesse Vincent. + + +1.16 2003-09-25 + +- David Wheeler's patch exposed a bug in Devel::StackTrace which could + cause exceptions to not have any value set for package, file, or + line. This is fixed by depending on Devel::StackTrace 1.04. This + release is otherwise identical to 1.15. Reported by Steve Hay. + + +1.15 2003-09-17 + +- Exceptions thrown from an alias subroutine always had + "Exception::Class" as their package name. Patch by David Wheeler. + + +1.14 2003-07-04 + +- ** BACKWARDS INCOMPATIBILITY ** + NoRefs is now true by default. This means that stacktraces will + _not_ store references unless you explicitly set NoRefs to a false + value. This is done because it's too easy to leak memory when + references are saved in stack traces. + +- Fixed a very odd bug where Exception::Class could mistakenly think + that an exception class had been created when it really hadn't. + This was only exposed with bleadperl (5.8.1-to-be), and is unlikely + to affect anyone using an existing Perl version. + + +1.13 2003-06-21 + +- Documented the Exception::Class::Base->Fields method, which can be + called on subclasses to determine what extra fields they have + defined. Suggested by Chris Winters. + + +1.12 2003-04-03 + +- Added the Exception::Class::Base->Classes method. Patch by David + Wheeler. + + +1.11 2003-02-20 + +- A description containing a single quote or backslash would cause a + syntax error in the code generated by Exception::Class. Patch by + Luc St-Louis. + + +1.10 2003-01-28 + +- The alias feature had a bug that showed up when an alias was + assigned to a class whose parent had not yet been made. + + +1.09 2003-01-22 + +- Tweak to Fields method so that it returns empty list for 5.6.1 and + 5.00503. + +- Require Devel::StackTrace 1.03 to fix potential recursion problem + between the two classes when stringifying exceptions. + + +1.08 2003-01-21 + +- Added "alias" feature, which allows you to create a subroutine that + throws a specified exception. This is based on code created by Ken + Williams for Mason. + + +1.07 2002-09-19 + +- Forgot to require Devel::StackTrace 1.01. + + +1.06 2002-09-19 + +- Use File::Spec when testing file method so that test 7 passes on all + platforms. Reported by Ron Savage. + +- Use Test::More for test suite. + +- Change NoObjectRefs method to NoRefs, to match change in + Devel::StackTrace 1.01. The old method is deprecated, but will + continue to work. However, it now _means_ the same thing as NoRefs. + + +1.05 2002-08-23 + +- Add NoObjectRefs class method to prevent Devel::StackTrace from + holding onto objects internally, thus delaying destruction. Based + on a discussion with Tatsuhiko Miyagawa. + + +1.04 2002-08-22 + +- Fix messed up tarball as reported by Tatsuhiko Miyagawa. + + +1.03 2002-08-21 + +- Some versions of Perl may give an error about "Ambiguous call + resolved as CORE::time()". This should be fixed now. Reported by + Ron Savage. + + +1.02 2002-08-20 + +- Fields set in a parent class were not actually being inherited by + children. Patch (with tests!) by David Wheeler. + +- Allow a single argument (error/message) to the constructor. Patch + by Tatsuhiko Miyagawa. + +- General doc cleanup. + + +1.01 2002-05-23 + +- Make sure that exception objects always test as true in a boolean + context. + + +1.00 2002-03-08 + +- Added a full_message method intended to be overridden when you + create a subclass that uses fields that you want included in the + stringification of the object. Suggested by Jon Swartz. + + +0.99 2002-03-06 + +- The much-requested "arbitrary attributes" feature has been added. + Thanks to Jon Swartz for the patch. You can now do this: + + use Exception::Class ( 'My::Exception' => { fields => [ 'bar', 'baz' ] } ); + + ... + + if ( UNIVERSAL::isa( $@, 'My::Exception' ) { print $@->bar, $@->baz; } + + +0.98 2002-02-20 + +- Subclasses that overrode the ->as_string method had to repeat the + stringification overloading for exception objects. This has been + changed so that the stringification overloading is now + + sub { $_[0]->as_string } + + Suggested by Jon Swartz. + + +0.97 2001-11-25 + +- Incorporated a patch from Ken Williams that works around several + Perl 5.6.0 bugs. + +- Test 31 was explicitly different for 5.6.0. Ken said that the + difference actually made it fail (whereas in the past for me it + succeeded). Tweak this test a bit more to just be more flexible + under 5.6.0. + + +0.96 2001-11-23 + +- Changes wasn't in MANIFEST. Oops. + +- Doc tweaks and cleanup of README. + +- Make do_trace a synonym to Trace for backwards compatibility. + + +0.95 2001-11-22 + + ** INCOMPATIBLE CHANGES IN THIS RELEASE ** + +- Remove do_trace method and replace it with Trace method, which is + inherited by subclasses. + +- Exception object now always have a Devel::StackTrace object + available. The Trace method only determines whether or not it is + included when the exception is stringified. + +- A new parameter show_trace for throw which allows you to override + the value of Trace on a case by case basis. + +- Fix uninit value warning with 5.00503. + + +0.90 2001-10-23 + +- fix buglet reported by Terrence Brannon that could occur while + trying to figure out where the exception was thrown, if the + exception was thrown from the package that the exception is an + object of. + +- doc improvements suggested Nathaniel Smith (related to using + Error.pm) and Mark Stosberg. + + +0.85 Unknown Release Date + +- added ability to use message instead of error as hash key to + constructor. + + +0.8 2000-09-03 + +- changed object from psuedo-hash to regular hash + +- added package, file, & line methods to exception object. + +- added more tests for exception object accessors + + +0.7 2000-09-03 + +- hack in test.pl to allow tests to pass under 5.6.0. + +- README details various Perl bugs this module (along with + Devel::StackTrace) can expose and some possible workarounds. + + +0.6 2000-06-27 + +- renamed to Exception::Class + + +0.5 2000-06-26 + +- Initial release, as Class::Exceptions. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..d444ad3 --- /dev/null +++ b/INSTALL @@ -0,0 +1,43 @@ +This is the Perl distribution Exception-Class. + +Installing Exception-Class is straightforward. + +## Installation with cpanm + +If you have cpanm, you only need one line: + + % cpanm Exception::Class + +If it does not have permission to install modules to the current perl, cpanm +will automatically set up and install to a local::lib in your home directory. +See the local::lib documentation (https://metacpan.org/pod/local::lib) for +details on enabling it in your environment. + +## Installing with the CPAN shell + +Alternatively, if your CPAN shell is set up, you should just be able to do: + + % cpan Exception::Class + +## 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 your perl is system-managed, you can create a local::lib in your home +directory to install modules to. For details, see the local::lib documentation: +https://metacpan.org/pod/local::lib + +## Documentation + +Exception-Class documentation is available as POD. +You can run perldoc from a shell to read the documentation: + + % perldoc Exception::Class diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20ae53c --- /dev/null +++ b/LICENSE @@ -0,0 +1,379 @@ +This software is copyright (c) 2017 by Dave Rolsky. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +Terms of the Perl programming language system itself + +a) the GNU General Public License as published by the Free + Software Foundation; either version 1, or (at your option) any + later version, or +b) the "Artistic License" + +--- The GNU General Public License, Version 1, February 1989 --- + +This software is Copyright (c) 2017 by Dave Rolsky. + +This is free software, licensed under: + + The GNU General Public License, Version 1, February 1989 + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! + + +--- The Artistic License 1.0 --- + +This software is Copyright (c) 2017 by Dave Rolsky. + +This is free software, licensed under: + + The Artistic License 1.0 + +The Artistic License + +Preamble + +The intent of this document is to state the conditions under which a Package +may be copied, such that the Copyright Holder maintains some semblance of +artistic control over the development of the package, while giving the users of +the package the right to use and distribute the Package in a more-or-less +customary fashion, plus the right to make reasonable modifications. + +Definitions: + + - "Package" refers to the collection of files distributed by the Copyright + Holder, and derivatives of that collection of files created through + textual modification. + - "Standard Version" refers to such a Package if it has not been modified, + or has been modified in accordance with the wishes of the Copyright + Holder. + - "Copyright Holder" is whoever is named in the copyright or copyrights for + the package. + - "You" is you, if you're thinking about copying or distributing this Package. + - "Reasonable copying fee" is whatever you can justify on the basis of media + cost, duplication charges, time of people involved, and so on. (You will + not be required to justify it to the Copyright Holder, but only to the + computing community at large as a market that must bear the fee.) + - "Freely Available" means that no fee is charged for the item itself, though + there may be fees involved in handling the item. It also means that + recipients of the item may redistribute it under the same conditions they + received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications derived +from the Public Domain or from the Copyright Holder. A Package modified in such +a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided that +you insert a prominent notice in each changed file stating how and when you +changed that file, and provided that you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or an + equivalent medium, or placing the modifications on a major archive site + such as ftp.uu.net, or by allowing the Copyright Holder to include your + modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict with + standard executables, which must also be provided, and provide a separate + manual page for each non-standard executable that clearly documents how it + differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or executable +form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where to + get the Standard Version. + + b) accompany the distribution with the machine-readable source of the Package + with your modifications. + + c) accompany any non-standard executables with their corresponding Standard + Version executables, giving the non-standard executables non-standard + names, and clearly documenting the differences in manual pages (or + equivalent), together with instructions on where to get the Standard + Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this Package. You +may not charge a fee for this Package itself. However, you may distribute this +Package in aggregate with other (possibly commercial) programs as part of a +larger (possibly commercial) software distribution provided that you do not +advertise this Package as a product of your own. + +6. The scripts and library files supplied as input to or produced as output +from the programs of this Package do not automatically fall under the copyright +of this Package, but belong to whomever generated them, and may be sold +commercially, and may be aggregated with this Package. + +7. C or perl subroutines supplied by you and linked into this Package shall not +be considered part of this Package. + +8. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +The End + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..6f7d9a9 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,38 @@ +# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.010. +CONTRIBUTING.md +Changes +INSTALL +LICENSE +MANIFEST +META.json +META.yml +Makefile.PL +README.md +appveyor.yml +bench/simple +cpanfile +dist.ini +lib/Exception/Class.pm +lib/Exception/Class/Base.pm +perlcriticrc +perltidyrc +t/00-report-prereqs.dd +t/00-report-prereqs.t +t/basic.t +t/caught.t +t/context.t +t/ecb-standalone.t +t/ignore.t +tidyall.ini +xt/author/00-compile.t +xt/author/eol.t +xt/author/mojibake.t +xt/author/no-tabs.t +xt/author/pod-coverage.t +xt/author/pod-spell.t +xt/author/pod-syntax.t +xt/author/portability.t +xt/author/test-version.t +xt/author/tidyall.t +xt/release/cpan-changes.t +xt/release/meta-json.t diff --git a/META.json b/META.json new file mode 100644 index 0000000..86ec81c --- /dev/null +++ b/META.json @@ -0,0 +1,1010 @@ +{ + "abstract" : "A module that allows you to declare real exception classes in Perl", + "author" : [ + "Dave Rolsky " + ], + "dynamic_config" : 0, + "generated_by" : "Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : 2 + }, + "name" : "Exception-Class", + "prereqs" : { + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0" + } + }, + "develop" : { + "requires" : { + "Code::TidyAll" : "0.56", + "Code::TidyAll::Plugin::SortLines::Naturally" : "0.000003", + "Code::TidyAll::Plugin::Test::Vars" : "0.02", + "File::Spec" : "0", + "IO::Handle" : "0", + "IPC::Open3" : "0", + "Parallel::ForkManager" : "1.19", + "Perl::Critic" : "1.126", + "Perl::Tidy" : "20160302", + "Pod::Coverage::TrustPod" : "0", + "Pod::Wordlist" : "0", + "Test::CPAN::Changes" : "0.19", + "Test::CPAN::Meta::JSON" : "0.16", + "Test::Code::TidyAll" : "0.50", + "Test::EOL" : "0", + "Test::Mojibake" : "0", + "Test::More" : "0.96", + "Test::NoTabs" : "0", + "Test::Pod" : "1.41", + "Test::Pod::Coverage" : "1.08", + "Test::Portability::Files" : "0", + "Test::Spelling" : "0.12", + "Test::Vars" : "0.009", + "Test::Version" : "2.05" + } + }, + "runtime" : { + "requires" : { + "Class::Data::Inheritable" : "0.02", + "Devel::StackTrace" : "2.00", + "Scalar::Util" : "0", + "base" : "0", + "overload" : "0", + "perl" : "5.008001", + "strict" : "0", + "warnings" : "0" + } + }, + "test" : { + "recommends" : { + "CPAN::Meta" : "2.120900" + }, + "requires" : { + "ExtUtils::MakeMaker" : "0", + "File::Spec" : "0", + "Test::More" : "0.96" + } + } + }, + "provides" : { + "Exception::Class" : { + "file" : "lib/Exception/Class.pm", + "version" : "1.44" + }, + "Exception::Class::Base" : { + "file" : "lib/Exception/Class/Base.pm", + "version" : "1.44" + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "web" : "https://github.com/houseabsolute/Exception-Class/issues" + }, + "homepage" : "http://metacpan.org/release/Exception-Class", + "repository" : { + "type" : "git", + "url" : "git://github.com/houseabsolute/Exception-Class.git", + "web" : "https://github.com/houseabsolute/Exception-Class" + } + }, + "version" : "1.44", + "x_Dist_Zilla" : { + "perl" : { + "version" : "5.026001" + }, + "plugins" : [ + { + "class" : "Dist::Zilla::Plugin::MakeMaker", + "config" : { + "Dist::Zilla::Role::TestRunner" : { + "default_jobs" : 1 + } + }, + "name" : "@DROLSKY/MakeMaker", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::Git::GatherDir", + "config" : { + "Dist::Zilla::Plugin::GatherDir" : { + "exclude_filename" : [ + "CONTRIBUTING.md", + "LICENSE", + "Makefile.PL", + "README.md", + "cpanfile" + ], + "exclude_match" : [], + "follow_symlinks" : 0, + "include_dotfiles" : 0, + "prefix" : "", + "prune_directory" : [], + "root" : "." + }, + "Dist::Zilla::Plugin::Git::GatherDir" : { + "include_untracked" : 0 + } + }, + "name" : "@DROLSKY/Git::GatherDir", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::ManifestSkip", + "name" : "@DROLSKY/ManifestSkip", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::License", + "name" : "@DROLSKY/License", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::ExecDir", + "name" : "@DROLSKY/ExecDir", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::ShareDir", + "name" : "@DROLSKY/ShareDir", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::Manifest", + "name" : "@DROLSKY/Manifest", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::CheckVersionIncrement", + "name" : "@DROLSKY/CheckVersionIncrement", + "version" : "0.121750" + }, + { + "class" : "Dist::Zilla::Plugin::TestRelease", + "name" : "@DROLSKY/TestRelease", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::ConfirmRelease", + "name" : "@DROLSKY/ConfirmRelease", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::UploadToCPAN", + "name" : "@DROLSKY/UploadToCPAN", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::VersionFromMainModule", + "name" : "@DROLSKY/VersionFromMainModule", + "version" : "0.03" + }, + { + "class" : "Dist::Zilla::Plugin::Authority", + "name" : "@DROLSKY/Authority", + "version" : "1.009" + }, + { + "class" : "Dist::Zilla::Plugin::AutoPrereqs", + "name" : "@DROLSKY/AutoPrereqs", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::CopyFilesFromBuild", + "name" : "@DROLSKY/CopyFilesFromBuild", + "version" : "0.170880" + }, + { + "class" : "Dist::Zilla::Plugin::GitHub::Meta", + "name" : "@DROLSKY/GitHub::Meta", + "version" : "0.44" + }, + { + "class" : "Dist::Zilla::Plugin::GitHub::Update", + "config" : { + "Dist::Zilla::Plugin::GitHub::Update" : { + "metacpan" : 1 + } + }, + "name" : "@DROLSKY/GitHub::Update", + "version" : "0.44" + }, + { + "class" : "Dist::Zilla::Plugin::MetaResources", + "name" : "@DROLSKY/MetaResources", + "version" : "6.010" + }, + { + "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" : "6.010" + } + ], + "include_underscores" : 0 + }, + "Dist::Zilla::Role::MetaProvider::Provider" : { + "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", + "inherit_missing" : 1, + "inherit_version" : 1, + "meta_noindex" : 1 + }, + "Dist::Zilla::Role::ModuleMetadata" : { + "Module::Metadata" : "1.000033", + "version" : "0.004" + } + }, + "name" : "@DROLSKY/MetaProvides::Package", + "version" : "2.004003" + }, + { + "class" : "Dist::Zilla::Plugin::Meta::Contributors", + "name" : "@DROLSKY/Meta::Contributors", + "version" : "0.003" + }, + { + "class" : "Dist::Zilla::Plugin::MetaConfig", + "name" : "@DROLSKY/MetaConfig", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::MetaJSON", + "name" : "@DROLSKY/MetaJSON", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::MetaYAML", + "name" : "@DROLSKY/MetaYAML", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::NextRelease", + "name" : "@DROLSKY/NextRelease", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::Prereqs", + "config" : { + "Dist::Zilla::Plugin::Prereqs" : { + "phase" : "test", + "type" : "requires" + } + }, + "name" : "@DROLSKY/Test::More with subtest", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::Prereqs", + "config" : { + "Dist::Zilla::Plugin::Prereqs" : { + "phase" : "develop", + "type" : "requires" + } + }, + "name" : "@DROLSKY/Modules for use with tidyall", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::Prereqs", + "config" : { + "Dist::Zilla::Plugin::Prereqs" : { + "phase" : "develop", + "type" : "requires" + } + }, + "name" : "@DROLSKY/Test::Version which fixes https://github.com/plicease/Test-Version/issues/7", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::PromptIfStale", + "config" : { + "Dist::Zilla::Plugin::PromptIfStale" : { + "check_all_plugins" : 0, + "check_all_prereqs" : 0, + "modules" : [ + "Dist::Zilla::PluginBundle::DROLSKY" + ], + "phase" : "build", + "run_under_travis" : 0, + "skip" : [] + } + }, + "name" : "@DROLSKY/Dist::Zilla::PluginBundle::DROLSKY", + "version" : "0.054" + }, + { + "class" : "Dist::Zilla::Plugin::PromptIfStale", + "config" : { + "Dist::Zilla::Plugin::PromptIfStale" : { + "check_all_plugins" : 1, + "check_all_prereqs" : 1, + "modules" : [], + "phase" : "release", + "run_under_travis" : 0, + "skip" : [ + "Dist::Zilla::Plugin::DROLSKY::Contributors", + "Dist::Zilla::Plugin::DROLSKY::Git::CheckFor::CorrectBranch", + "Dist::Zilla::Plugin::DROLSKY::License", + "Dist::Zilla::Plugin::DROLSKY::TidyAll", + "Dist::Zilla::Plugin::DROLSKY::WeaverConfig", + "Pod::Weaver::PluginBundle::DROLSKY" + ] + } + }, + "name" : "@DROLSKY/PromptIfStale", + "version" : "0.054" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable", + "name" : "@DROLSKY/Test::Pod::Coverage::Configurable", + "version" : "0.06" + }, + { + "class" : "Dist::Zilla::Plugin::Test::PodSpelling", + "config" : { + "Dist::Zilla::Plugin::Test::PodSpelling" : { + "directories" : [ + "bin", + "lib" + ], + "spell_cmd" : "", + "stopwords" : [ + "Arun", + "CPAN", + "DROLSKY", + "DROLSKY's", + "Kumar's", + "PayPal", + "PayPal", + "Rolsky", + "Rolsky", + "Rolsky's", + "Uncatchable", + "automagic", + "drolsky", + "egid", + "esque", + "isa", + "namespace", + "param", + "refcount", + "rethrow", + "runtime", + "stacktrace", + "stringifies", + "subclasses", + "uncatchable" + ], + "wordlist" : "Pod::Wordlist" + } + }, + "name" : "@DROLSKY/Test::PodSpelling", + "version" : "2.007004" + }, + { + "class" : "Dist::Zilla::Plugin::PodSyntaxTests", + "name" : "@DROLSKY/PodSyntaxTests", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::RunExtraTests", + "config" : { + "Dist::Zilla::Role::TestRunner" : { + "default_jobs" : 1 + } + }, + "name" : "@DROLSKY/RunExtraTests", + "version" : "0.029" + }, + { + "class" : "Dist::Zilla::Plugin::MojibakeTests", + "name" : "@DROLSKY/MojibakeTests", + "version" : "0.8" + }, + { + "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes", + "config" : { + "Dist::Zilla::Plugin::Test::CPAN::Changes" : { + "changelog" : "Changes" + } + }, + "name" : "@DROLSKY/Test::CPAN::Changes", + "version" : "0.012" + }, + { + "class" : "Dist::Zilla::Plugin::Test::CPAN::Meta::JSON", + "name" : "@DROLSKY/Test::CPAN::Meta::JSON", + "version" : "0.004" + }, + { + "class" : "Dist::Zilla::Plugin::Test::EOL", + "config" : { + "Dist::Zilla::Plugin::Test::EOL" : { + "filename" : "xt/author/eol.t", + "finder" : [ + ":ExecFiles", + ":InstallModules", + ":TestFiles" + ], + "trailing_whitespace" : 1 + } + }, + "name" : "@DROLSKY/Test::EOL", + "version" : "0.19" + }, + { + "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.15" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Portability", + "config" : { + "Dist::Zilla::Plugin::Test::Portability" : { + "options" : "" + } + }, + "name" : "@DROLSKY/Test::Portability", + "version" : "2.001000" + }, + { + "class" : "Dist::Zilla::Plugin::Test::TidyAll", + "name" : "@DROLSKY/Test::TidyAll", + "version" : "0.04" + }, + { + "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" : "xt/author/00-compile.t", + "module_finder" : [ + ":InstallModules" + ], + "needs_display" : 0, + "phase" : "develop", + "script_finder" : [ + ":PerlExecFiles" + ], + "skips" : [], + "switch" : [] + } + }, + "name" : "@DROLSKY/Test::Compile", + "version" : "2.057" + }, + { + "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", + "name" : "@DROLSKY/Test::ReportPrereqs", + "version" : "0.027" + }, + { + "class" : "Dist::Zilla::Plugin::Test::Version", + "name" : "@DROLSKY/Test::Version", + "version" : "1.09" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::Contributors", + "name" : "@DROLSKY/DROLSKY::Contributors", + "version" : "0.88" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Contributors", + "config" : { + "Dist::Zilla::Plugin::Git::Contributors" : { + "git_version" : "2.15.1", + "include_authors" : 0, + "include_releaser" : 1, + "order_by" : "name", + "paths" : [] + } + }, + "name" : "@DROLSKY/Git::Contributors", + "version" : "0.032" + }, + { + "class" : "Dist::Zilla::Plugin::SurgicalPodWeaver", + "config" : { + "Dist::Zilla::Plugin::PodWeaver" : { + "config_plugins" : [ + "@DROLSKY" + ], + "finder" : [ + ":InstallModules", + ":ExecFiles" + ], + "plugins" : [ + { + "class" : "Pod::Weaver::Plugin::EnsurePod5", + "name" : "@CorePrep/EnsurePod5", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Plugin::H1Nester", + "name" : "@CorePrep/H1Nester", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Plugin::SingleEncoding", + "name" : "@DROLSKY/SingleEncoding", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Plugin::Transformer", + "name" : "@DROLSKY/List", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Plugin::Transformer", + "name" : "@DROLSKY/Verbatim", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "@DROLSKY/header", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Name", + "name" : "@DROLSKY/Name", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Version", + "name" : "@DROLSKY/Version", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "@DROLSKY/prelude", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Generic", + "name" : "SYNOPSIS", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Generic", + "name" : "DESCRIPTION", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Generic", + "name" : "OVERVIEW", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Collect", + "name" : "ATTRIBUTES", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Collect", + "name" : "METHODS", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Collect", + "name" : "FUNCTIONS", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Collect", + "name" : "TYPES", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Leftovers", + "name" : "@DROLSKY/Leftovers", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "@DROLSKY/postlude", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::GenerateSection", + "name" : "@DROLSKY/generate SUPPORT", + "version" : "1.06" + }, + { + "class" : "Pod::Weaver::Section::AllowOverride", + "name" : "@DROLSKY/allow override SUPPORT", + "version" : "0.05" + }, + { + "class" : "Pod::Weaver::Section::GenerateSection", + "name" : "@DROLSKY/generate SOURCE", + "version" : "1.06" + }, + { + "class" : "Pod::Weaver::Section::GenerateSection", + "name" : "@DROLSKY/generate DONATIONS", + "version" : "1.06" + }, + { + "class" : "Pod::Weaver::Section::Authors", + "name" : "@DROLSKY/Authors", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Contributors", + "name" : "@DROLSKY/Contributors", + "version" : "0.009" + }, + { + "class" : "Pod::Weaver::Section::Legal", + "name" : "@DROLSKY/Legal", + "version" : "4.015" + }, + { + "class" : "Pod::Weaver::Section::Region", + "name" : "@DROLSKY/footer", + "version" : "4.015" + } + ] + } + }, + "name" : "@DROLSKY/SurgicalPodWeaver", + "version" : "0.0023" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::WeaverConfig", + "name" : "@DROLSKY/DROLSKY::WeaverConfig", + "version" : "0.88" + }, + { + "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", + "config" : { + "Dist::Zilla::Role::FileWatcher" : { + "version" : "0.006" + } + }, + "name" : "@DROLSKY/README.md in build", + "version" : "0.163250" + }, + { + "class" : "Dist::Zilla::Plugin::GenerateFile::FromShareDir", + "config" : { + "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : { + "destination_filename" : "CONTRIBUTING.md", + "dist" : "Dist-Zilla-PluginBundle-DROLSKY", + "encoding" : "UTF-8", + "has_xs" : 0, + "location" : "build", + "source_filename" : "CONTRIBUTING.md" + }, + "Dist::Zilla::Role::RepoFileInjector" : { + "allow_overwrite" : 1, + "repo_root" : ".", + "version" : "0.007" + } + }, + "name" : "@DROLSKY/Generate CONTRIBUTING.md", + "version" : "0.013" + }, + { + "class" : "Dist::Zilla::Plugin::InstallGuide", + "name" : "@DROLSKY/InstallGuide", + "version" : "1.200007" + }, + { + "class" : "Dist::Zilla::Plugin::CPANFile", + "name" : "@DROLSKY/CPANFile", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::License", + "name" : "@DROLSKY/DROLSKY::License", + "version" : "0.88" + }, + { + "class" : "Dist::Zilla::Plugin::CheckStrictVersion", + "name" : "@DROLSKY/CheckStrictVersion", + "version" : "0.001" + }, + { + "class" : "Dist::Zilla::Plugin::CheckSelfDependency", + "config" : { + "Dist::Zilla::Plugin::CheckSelfDependency" : { + "finder" : [ + ":InstallModules" + ] + }, + "Dist::Zilla::Role::ModuleMetadata" : { + "Module::Metadata" : "1.000033", + "version" : "0.004" + } + }, + "name" : "@DROLSKY/CheckSelfDependency", + "version" : "0.011" + }, + { + "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", + "name" : "@DROLSKY/CheckPrereqsIndexed", + "version" : "0.020" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::Git::CheckFor::CorrectBranch", + "config" : { + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + } + }, + "name" : "@DROLSKY/DROLSKY::Git::CheckFor::CorrectBranch", + "version" : "0.88" + }, + { + "class" : "Dist::Zilla::Plugin::EnsureChangesHasContent", + "name" : "@DROLSKY/EnsureChangesHasContent", + "version" : "0.02" + }, + { + "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts", + "config" : { + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::CheckFor::MergeConflicts", + "version" : "0.014" + }, + { + "class" : "Dist::Zilla::Plugin::DROLSKY::TidyAll", + "name" : "@DROLSKY/DROLSKY::TidyAll", + "version" : "0.88" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Check", + "config" : { + "Dist::Zilla::Plugin::Git::Check" : { + "untracked_files" : "die" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "CONTRIBUTING.md", + "Changes", + "LICENSE", + "Makefile.PL", + "README.md", + "cpanfile", + "tidyall.ini" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Check", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Commit", + "config" : { + "Dist::Zilla::Plugin::Git::Commit" : { + "add_files_in" : [], + "commit_msg" : "v%v%n%n%c" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "CONTRIBUTING.md", + "Changes", + "LICENSE", + "Makefile.PL", + "README.md", + "cpanfile", + "tidyall.ini" + ], + "allow_dirty_match" : [], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + }, + "Dist::Zilla::Role::Git::StringFormatter" : { + "time_zone" : "local" + } + }, + "name" : "@DROLSKY/Commit generated files", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Tag", + "config" : { + "Dist::Zilla::Plugin::Git::Tag" : { + "branch" : null, + "changelog" : "Changes", + "signed" : 0, + "tag" : "v1.44", + "tag_format" : "v%v", + "tag_message" : "v%v" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + }, + "Dist::Zilla::Role::Git::StringFormatter" : { + "time_zone" : "local" + } + }, + "name" : "@DROLSKY/Git::Tag", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Push", + "config" : { + "Dist::Zilla::Plugin::Git::Push" : { + "push_to" : [ + "origin" + ], + "remotes_must_exist" : 1 + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Git::Push", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease", + "config" : { + "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { + "finders" : [ + ":ExecFiles", + ":InstallModules" + ], + "global" : 0, + "munge_makefile_pl" : 1 + } + }, + "name" : "@DROLSKY/BumpVersionAfterRelease", + "version" : "0.016" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Commit", + "config" : { + "Dist::Zilla::Plugin::Git::Commit" : { + "add_files_in" : [], + "commit_msg" : "Bump version after release" + }, + "Dist::Zilla::Role::Git::DirtyFiles" : { + "allow_dirty" : [ + "Changes", + "dist.ini" + ], + "allow_dirty_match" : [ + "(?^:.+)" + ], + "changelog" : "Changes" + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + }, + "Dist::Zilla::Role::Git::StringFormatter" : { + "time_zone" : "local" + } + }, + "name" : "@DROLSKY/Commit version bump", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::Git::Push", + "config" : { + "Dist::Zilla::Plugin::Git::Push" : { + "push_to" : [ + "origin" + ], + "remotes_must_exist" : 1 + }, + "Dist::Zilla::Role::Git::Repo" : { + "git_version" : "2.15.1", + "repo_root" : "." + } + }, + "name" : "@DROLSKY/Push version bump", + "version" : "2.043" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":InstallModules", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":IncModules", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":TestFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ExtraTestFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ExecFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":PerlExecFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":ShareFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":MainModule", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":AllFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : ":NoFiles", + "version" : "6.010" + }, + { + "class" : "Dist::Zilla::Plugin::FinderCode", + "name" : "@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM", + "version" : "6.010" + } + ], + "zilla" : { + "class" : "Dist::Zilla::Dist::Builder", + "config" : { + "is_trial" : 0 + }, + "version" : "6.010" + } + }, + "x_authority" : "cpan:DROLSKY", + "x_contributors" : [ + "Alexander Batyrshin <0x62ash@gmail.com>", + "Leon Timmermans ", + "Ricardo Signes " + ], + "x_serialization_backend" : "Cpanel::JSON::XS version 3.0239" +} + diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..b6b81fe --- /dev/null +++ b/META.yml @@ -0,0 +1,743 @@ +--- +abstract: 'A module that allows you to declare real exception classes in Perl' +author: + - 'Dave Rolsky ' +build_requires: + ExtUtils::MakeMaker: '0' + File::Spec: '0' + Test::More: '0.96' +configure_requires: + ExtUtils::MakeMaker: '0' +dynamic_config: 0 +generated_by: 'Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010' +license: perl +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: Exception-Class +provides: + Exception::Class: + file: lib/Exception/Class.pm + version: '1.44' + Exception::Class::Base: + file: lib/Exception/Class/Base.pm + version: '1.44' +requires: + Class::Data::Inheritable: '0.02' + Devel::StackTrace: '2.00' + Scalar::Util: '0' + base: '0' + overload: '0' + perl: '5.008001' + strict: '0' + warnings: '0' +resources: + bugtracker: https://github.com/houseabsolute/Exception-Class/issues + homepage: http://metacpan.org/release/Exception-Class + repository: git://github.com/houseabsolute/Exception-Class.git +version: '1.44' +x_Dist_Zilla: + perl: + version: '5.026001' + plugins: + - + class: Dist::Zilla::Plugin::MakeMaker + config: + Dist::Zilla::Role::TestRunner: + default_jobs: 1 + name: '@DROLSKY/MakeMaker' + version: '6.010' + - + class: Dist::Zilla::Plugin::Git::GatherDir + config: + Dist::Zilla::Plugin::GatherDir: + exclude_filename: + - CONTRIBUTING.md + - LICENSE + - Makefile.PL + - README.md + - cpanfile + exclude_match: [] + follow_symlinks: 0 + include_dotfiles: 0 + prefix: '' + prune_directory: [] + root: . + Dist::Zilla::Plugin::Git::GatherDir: + include_untracked: 0 + name: '@DROLSKY/Git::GatherDir' + version: '2.043' + - + class: Dist::Zilla::Plugin::ManifestSkip + name: '@DROLSKY/ManifestSkip' + version: '6.010' + - + class: Dist::Zilla::Plugin::License + name: '@DROLSKY/License' + version: '6.010' + - + class: Dist::Zilla::Plugin::ExecDir + name: '@DROLSKY/ExecDir' + version: '6.010' + - + class: Dist::Zilla::Plugin::ShareDir + name: '@DROLSKY/ShareDir' + version: '6.010' + - + class: Dist::Zilla::Plugin::Manifest + name: '@DROLSKY/Manifest' + version: '6.010' + - + class: Dist::Zilla::Plugin::CheckVersionIncrement + name: '@DROLSKY/CheckVersionIncrement' + version: '0.121750' + - + class: Dist::Zilla::Plugin::TestRelease + name: '@DROLSKY/TestRelease' + version: '6.010' + - + class: Dist::Zilla::Plugin::ConfirmRelease + name: '@DROLSKY/ConfirmRelease' + version: '6.010' + - + class: Dist::Zilla::Plugin::UploadToCPAN + name: '@DROLSKY/UploadToCPAN' + version: '6.010' + - + class: Dist::Zilla::Plugin::VersionFromMainModule + name: '@DROLSKY/VersionFromMainModule' + version: '0.03' + - + class: Dist::Zilla::Plugin::Authority + name: '@DROLSKY/Authority' + version: '1.009' + - + class: Dist::Zilla::Plugin::AutoPrereqs + name: '@DROLSKY/AutoPrereqs' + version: '6.010' + - + class: Dist::Zilla::Plugin::CopyFilesFromBuild + name: '@DROLSKY/CopyFilesFromBuild' + version: '0.170880' + - + class: Dist::Zilla::Plugin::GitHub::Meta + name: '@DROLSKY/GitHub::Meta' + version: '0.44' + - + class: Dist::Zilla::Plugin::GitHub::Update + config: + Dist::Zilla::Plugin::GitHub::Update: + metacpan: 1 + name: '@DROLSKY/GitHub::Update' + version: '0.44' + - + class: Dist::Zilla::Plugin::MetaResources + name: '@DROLSKY/MetaResources' + version: '6.010' + - + 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: '6.010' + include_underscores: 0 + Dist::Zilla::Role::MetaProvider::Provider: + $Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004' + inherit_missing: '1' + inherit_version: '1' + meta_noindex: '1' + Dist::Zilla::Role::ModuleMetadata: + Module::Metadata: '1.000033' + version: '0.004' + name: '@DROLSKY/MetaProvides::Package' + version: '2.004003' + - + class: Dist::Zilla::Plugin::Meta::Contributors + name: '@DROLSKY/Meta::Contributors' + version: '0.003' + - + class: Dist::Zilla::Plugin::MetaConfig + name: '@DROLSKY/MetaConfig' + version: '6.010' + - + class: Dist::Zilla::Plugin::MetaJSON + name: '@DROLSKY/MetaJSON' + version: '6.010' + - + class: Dist::Zilla::Plugin::MetaYAML + name: '@DROLSKY/MetaYAML' + version: '6.010' + - + class: Dist::Zilla::Plugin::NextRelease + name: '@DROLSKY/NextRelease' + version: '6.010' + - + class: Dist::Zilla::Plugin::Prereqs + config: + Dist::Zilla::Plugin::Prereqs: + phase: test + type: requires + name: '@DROLSKY/Test::More with subtest' + version: '6.010' + - + class: Dist::Zilla::Plugin::Prereqs + config: + Dist::Zilla::Plugin::Prereqs: + phase: develop + type: requires + name: '@DROLSKY/Modules for use with tidyall' + version: '6.010' + - + class: Dist::Zilla::Plugin::Prereqs + config: + Dist::Zilla::Plugin::Prereqs: + phase: develop + type: requires + name: '@DROLSKY/Test::Version which fixes https://github.com/plicease/Test-Version/issues/7' + version: '6.010' + - + class: Dist::Zilla::Plugin::PromptIfStale + config: + Dist::Zilla::Plugin::PromptIfStale: + check_all_plugins: 0 + check_all_prereqs: 0 + modules: + - Dist::Zilla::PluginBundle::DROLSKY + phase: build + run_under_travis: 0 + skip: [] + name: '@DROLSKY/Dist::Zilla::PluginBundle::DROLSKY' + version: '0.054' + - + class: Dist::Zilla::Plugin::PromptIfStale + config: + Dist::Zilla::Plugin::PromptIfStale: + check_all_plugins: 1 + check_all_prereqs: 1 + modules: [] + phase: release + run_under_travis: 0 + skip: + - Dist::Zilla::Plugin::DROLSKY::Contributors + - Dist::Zilla::Plugin::DROLSKY::Git::CheckFor::CorrectBranch + - Dist::Zilla::Plugin::DROLSKY::License + - Dist::Zilla::Plugin::DROLSKY::TidyAll + - Dist::Zilla::Plugin::DROLSKY::WeaverConfig + - Pod::Weaver::PluginBundle::DROLSKY + name: '@DROLSKY/PromptIfStale' + version: '0.054' + - + class: Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable + name: '@DROLSKY/Test::Pod::Coverage::Configurable' + version: '0.06' + - + class: Dist::Zilla::Plugin::Test::PodSpelling + config: + Dist::Zilla::Plugin::Test::PodSpelling: + directories: + - bin + - lib + spell_cmd: '' + stopwords: + - Arun + - CPAN + - DROLSKY + - "DROLSKY's" + - "Kumar's" + - PayPal + - PayPal + - Rolsky + - Rolsky + - "Rolsky's" + - Uncatchable + - automagic + - drolsky + - egid + - esque + - isa + - namespace + - param + - refcount + - rethrow + - runtime + - stacktrace + - stringifies + - subclasses + - uncatchable + wordlist: Pod::Wordlist + name: '@DROLSKY/Test::PodSpelling' + version: '2.007004' + - + class: Dist::Zilla::Plugin::PodSyntaxTests + name: '@DROLSKY/PodSyntaxTests' + version: '6.010' + - + class: Dist::Zilla::Plugin::RunExtraTests + config: + Dist::Zilla::Role::TestRunner: + default_jobs: 1 + name: '@DROLSKY/RunExtraTests' + version: '0.029' + - + class: Dist::Zilla::Plugin::MojibakeTests + name: '@DROLSKY/MojibakeTests' + version: '0.8' + - + class: Dist::Zilla::Plugin::Test::CPAN::Changes + config: + Dist::Zilla::Plugin::Test::CPAN::Changes: + changelog: Changes + name: '@DROLSKY/Test::CPAN::Changes' + version: '0.012' + - + class: Dist::Zilla::Plugin::Test::CPAN::Meta::JSON + name: '@DROLSKY/Test::CPAN::Meta::JSON' + version: '0.004' + - + class: Dist::Zilla::Plugin::Test::EOL + config: + Dist::Zilla::Plugin::Test::EOL: + filename: xt/author/eol.t + finder: + - ':ExecFiles' + - ':InstallModules' + - ':TestFiles' + trailing_whitespace: 1 + name: '@DROLSKY/Test::EOL' + version: '0.19' + - + 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.15' + - + class: Dist::Zilla::Plugin::Test::Portability + config: + Dist::Zilla::Plugin::Test::Portability: + options: '' + name: '@DROLSKY/Test::Portability' + version: '2.001000' + - + class: Dist::Zilla::Plugin::Test::TidyAll + name: '@DROLSKY/Test::TidyAll' + version: '0.04' + - + 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: xt/author/00-compile.t + module_finder: + - ':InstallModules' + needs_display: 0 + phase: develop + script_finder: + - ':PerlExecFiles' + skips: [] + switch: [] + name: '@DROLSKY/Test::Compile' + version: '2.057' + - + class: Dist::Zilla::Plugin::Test::ReportPrereqs + name: '@DROLSKY/Test::ReportPrereqs' + version: '0.027' + - + class: Dist::Zilla::Plugin::Test::Version + name: '@DROLSKY/Test::Version' + version: '1.09' + - + class: Dist::Zilla::Plugin::DROLSKY::Contributors + name: '@DROLSKY/DROLSKY::Contributors' + version: '0.88' + - + class: Dist::Zilla::Plugin::Git::Contributors + config: + Dist::Zilla::Plugin::Git::Contributors: + git_version: 2.15.1 + include_authors: 0 + include_releaser: 1 + order_by: name + paths: [] + name: '@DROLSKY/Git::Contributors' + version: '0.032' + - + class: Dist::Zilla::Plugin::SurgicalPodWeaver + config: + Dist::Zilla::Plugin::PodWeaver: + config_plugins: + - '@DROLSKY' + finder: + - ':InstallModules' + - ':ExecFiles' + plugins: + - + class: Pod::Weaver::Plugin::EnsurePod5 + name: '@CorePrep/EnsurePod5' + version: '4.015' + - + class: Pod::Weaver::Plugin::H1Nester + name: '@CorePrep/H1Nester' + version: '4.015' + - + class: Pod::Weaver::Plugin::SingleEncoding + name: '@DROLSKY/SingleEncoding' + version: '4.015' + - + class: Pod::Weaver::Plugin::Transformer + name: '@DROLSKY/List' + version: '4.015' + - + class: Pod::Weaver::Plugin::Transformer + name: '@DROLSKY/Verbatim' + version: '4.015' + - + class: Pod::Weaver::Section::Region + name: '@DROLSKY/header' + version: '4.015' + - + class: Pod::Weaver::Section::Name + name: '@DROLSKY/Name' + version: '4.015' + - + class: Pod::Weaver::Section::Version + name: '@DROLSKY/Version' + version: '4.015' + - + class: Pod::Weaver::Section::Region + name: '@DROLSKY/prelude' + version: '4.015' + - + class: Pod::Weaver::Section::Generic + name: SYNOPSIS + version: '4.015' + - + class: Pod::Weaver::Section::Generic + name: DESCRIPTION + version: '4.015' + - + class: Pod::Weaver::Section::Generic + name: OVERVIEW + version: '4.015' + - + class: Pod::Weaver::Section::Collect + name: ATTRIBUTES + version: '4.015' + - + class: Pod::Weaver::Section::Collect + name: METHODS + version: '4.015' + - + class: Pod::Weaver::Section::Collect + name: FUNCTIONS + version: '4.015' + - + class: Pod::Weaver::Section::Collect + name: TYPES + version: '4.015' + - + class: Pod::Weaver::Section::Leftovers + name: '@DROLSKY/Leftovers' + version: '4.015' + - + class: Pod::Weaver::Section::Region + name: '@DROLSKY/postlude' + version: '4.015' + - + class: Pod::Weaver::Section::GenerateSection + name: '@DROLSKY/generate SUPPORT' + version: '1.06' + - + class: Pod::Weaver::Section::AllowOverride + name: '@DROLSKY/allow override SUPPORT' + version: '0.05' + - + class: Pod::Weaver::Section::GenerateSection + name: '@DROLSKY/generate SOURCE' + version: '1.06' + - + class: Pod::Weaver::Section::GenerateSection + name: '@DROLSKY/generate DONATIONS' + version: '1.06' + - + class: Pod::Weaver::Section::Authors + name: '@DROLSKY/Authors' + version: '4.015' + - + class: Pod::Weaver::Section::Contributors + name: '@DROLSKY/Contributors' + version: '0.009' + - + class: Pod::Weaver::Section::Legal + name: '@DROLSKY/Legal' + version: '4.015' + - + class: Pod::Weaver::Section::Region + name: '@DROLSKY/footer' + version: '4.015' + name: '@DROLSKY/SurgicalPodWeaver' + version: '0.0023' + - + class: Dist::Zilla::Plugin::DROLSKY::WeaverConfig + name: '@DROLSKY/DROLSKY::WeaverConfig' + version: '0.88' + - + class: Dist::Zilla::Plugin::ReadmeAnyFromPod + config: + Dist::Zilla::Role::FileWatcher: + version: '0.006' + name: '@DROLSKY/README.md in build' + version: '0.163250' + - + class: Dist::Zilla::Plugin::GenerateFile::FromShareDir + config: + Dist::Zilla::Plugin::GenerateFile::FromShareDir: + destination_filename: CONTRIBUTING.md + dist: Dist-Zilla-PluginBundle-DROLSKY + encoding: UTF-8 + has_xs: '0' + location: build + source_filename: CONTRIBUTING.md + Dist::Zilla::Role::RepoFileInjector: + allow_overwrite: 1 + repo_root: . + version: '0.007' + name: '@DROLSKY/Generate CONTRIBUTING.md' + version: '0.013' + - + class: Dist::Zilla::Plugin::InstallGuide + name: '@DROLSKY/InstallGuide' + version: '1.200007' + - + class: Dist::Zilla::Plugin::CPANFile + name: '@DROLSKY/CPANFile' + version: '6.010' + - + class: Dist::Zilla::Plugin::DROLSKY::License + name: '@DROLSKY/DROLSKY::License' + version: '0.88' + - + class: Dist::Zilla::Plugin::CheckStrictVersion + name: '@DROLSKY/CheckStrictVersion' + version: '0.001' + - + class: Dist::Zilla::Plugin::CheckSelfDependency + config: + Dist::Zilla::Plugin::CheckSelfDependency: + finder: + - ':InstallModules' + Dist::Zilla::Role::ModuleMetadata: + Module::Metadata: '1.000033' + version: '0.004' + name: '@DROLSKY/CheckSelfDependency' + version: '0.011' + - + class: Dist::Zilla::Plugin::CheckPrereqsIndexed + name: '@DROLSKY/CheckPrereqsIndexed' + version: '0.020' + - + class: Dist::Zilla::Plugin::DROLSKY::Git::CheckFor::CorrectBranch + config: + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + name: '@DROLSKY/DROLSKY::Git::CheckFor::CorrectBranch' + version: '0.88' + - + class: Dist::Zilla::Plugin::EnsureChangesHasContent + name: '@DROLSKY/EnsureChangesHasContent' + version: '0.02' + - + class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts + config: + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + name: '@DROLSKY/Git::CheckFor::MergeConflicts' + version: '0.014' + - + class: Dist::Zilla::Plugin::DROLSKY::TidyAll + name: '@DROLSKY/DROLSKY::TidyAll' + version: '0.88' + - + class: Dist::Zilla::Plugin::Git::Check + config: + Dist::Zilla::Plugin::Git::Check: + untracked_files: die + Dist::Zilla::Role::Git::DirtyFiles: + allow_dirty: + - CONTRIBUTING.md + - Changes + - LICENSE + - Makefile.PL + - README.md + - cpanfile + - tidyall.ini + allow_dirty_match: [] + changelog: Changes + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + name: '@DROLSKY/Git::Check' + version: '2.043' + - + class: Dist::Zilla::Plugin::Git::Commit + config: + Dist::Zilla::Plugin::Git::Commit: + add_files_in: [] + commit_msg: v%v%n%n%c + Dist::Zilla::Role::Git::DirtyFiles: + allow_dirty: + - CONTRIBUTING.md + - Changes + - LICENSE + - Makefile.PL + - README.md + - cpanfile + - tidyall.ini + allow_dirty_match: [] + changelog: Changes + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + Dist::Zilla::Role::Git::StringFormatter: + time_zone: local + name: '@DROLSKY/Commit generated files' + version: '2.043' + - + class: Dist::Zilla::Plugin::Git::Tag + config: + Dist::Zilla::Plugin::Git::Tag: + branch: ~ + changelog: Changes + signed: 0 + tag: v1.44 + tag_format: v%v + tag_message: v%v + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + Dist::Zilla::Role::Git::StringFormatter: + time_zone: local + name: '@DROLSKY/Git::Tag' + version: '2.043' + - + class: Dist::Zilla::Plugin::Git::Push + config: + Dist::Zilla::Plugin::Git::Push: + push_to: + - origin + remotes_must_exist: 1 + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + name: '@DROLSKY/Git::Push' + version: '2.043' + - + class: Dist::Zilla::Plugin::BumpVersionAfterRelease + config: + Dist::Zilla::Plugin::BumpVersionAfterRelease: + finders: + - ':ExecFiles' + - ':InstallModules' + global: 0 + munge_makefile_pl: 1 + name: '@DROLSKY/BumpVersionAfterRelease' + version: '0.016' + - + class: Dist::Zilla::Plugin::Git::Commit + config: + Dist::Zilla::Plugin::Git::Commit: + add_files_in: [] + commit_msg: 'Bump version after release' + Dist::Zilla::Role::Git::DirtyFiles: + allow_dirty: + - Changes + - dist.ini + allow_dirty_match: + - (?^:.+) + changelog: Changes + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + Dist::Zilla::Role::Git::StringFormatter: + time_zone: local + name: '@DROLSKY/Commit version bump' + version: '2.043' + - + class: Dist::Zilla::Plugin::Git::Push + config: + Dist::Zilla::Plugin::Git::Push: + push_to: + - origin + remotes_must_exist: 1 + Dist::Zilla::Role::Git::Repo: + git_version: 2.15.1 + repo_root: . + name: '@DROLSKY/Push version bump' + version: '2.043' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':InstallModules' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':IncModules' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':TestFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':ExtraTestFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':ExecFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':PerlExecFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':ShareFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':MainModule' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':AllFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: ':NoFiles' + version: '6.010' + - + class: Dist::Zilla::Plugin::FinderCode + name: '@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM' + version: '6.010' + zilla: + class: Dist::Zilla::Dist::Builder + config: + is_trial: '0' + version: '6.010' +x_authority: cpan:DROLSKY +x_contributors: + - 'Alexander Batyrshin <0x62ash@gmail.com>' + - 'Leon Timmermans ' + - 'Ricardo Signes ' +x_serialization_backend: 'YAML::Tiny version 1.70' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..82f8e1a --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,63 @@ +# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.010. +use strict; +use warnings; + +use 5.008001; + +use ExtUtils::MakeMaker; + +my %WriteMakefileArgs = ( + "ABSTRACT" => "A module that allows you to declare real exception classes in Perl", + "AUTHOR" => "Dave Rolsky ", + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => 0 + }, + "DISTNAME" => "Exception-Class", + "LICENSE" => "perl", + "MIN_PERL_VERSION" => "5.008001", + "NAME" => "Exception::Class", + "PREREQ_PM" => { + "Class::Data::Inheritable" => "0.02", + "Devel::StackTrace" => "2.00", + "Scalar::Util" => 0, + "base" => 0, + "overload" => 0, + "strict" => 0, + "warnings" => 0 + }, + "TEST_REQUIRES" => { + "ExtUtils::MakeMaker" => 0, + "File::Spec" => 0, + "Test::More" => "0.96" + }, + "VERSION" => "1.44", + "test" => { + "TESTS" => "t/*.t" + } +); + + +my %FallbackPrereqs = ( + "Class::Data::Inheritable" => "0.02", + "Devel::StackTrace" => "2.00", + "ExtUtils::MakeMaker" => 0, + "File::Spec" => 0, + "Scalar::Util" => 0, + "Test::More" => "0.96", + "base" => 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..88576b4 --- /dev/null +++ b/README.md @@ -0,0 +1,328 @@ +# NAME + +Exception::Class - A module that allows you to declare real exception classes in Perl + +# VERSION + +version 1.44 + +# SYNOPSIS + + use Exception::Class ( + 'MyException', + + 'AnotherException' => { isa => 'MyException' }, + + 'YetAnotherException' => { + isa => 'AnotherException', + description => 'These exceptions are related to IPC' + }, + + 'ExceptionWithFields' => { + isa => 'YetAnotherException', + fields => [ 'grandiosity', 'quixotic' ], + alias => 'throw_fields', + }, + ); + use Scalar::Util qw( blessed ); + use Try::Tiny; + + try { + MyException->throw( error => 'I feel funny.' ); + } + catch { + die $_ unless blessed $_ && $_->can('rethrow'); + + if ( $_->isa('Exception::Class') ) { + warn $_->error, "\n", $_->trace->as_string, "\n"; + warn join ' ', $_->euid, $_->egid, $_->uid, $_->gid, $_->pid, $_->time; + + exit; + } + elsif ( $_->isa('ExceptionWithFields') ) { + if ( $_->quixotic ) { + handle_quixotic_exception(); + } + else { + handle_non_quixotic_exception(); + } + } + else { + $_->rethrow; + } + }; + + # without Try::Tiny + eval { ... }; + if ( my $e = Exception::Class->caught ) { ... } + + # use an alias - without parens subroutine name is checked at + # compile time + throw_fields error => "No strawberry", grandiosity => "quite a bit"; + +# DESCRIPTION + +**RECOMMENDATION 1**: If you are writing modern Perl code with [Moose](https://metacpan.org/pod/Moose) or +[Moo](https://metacpan.org/pod/Moo) I highly recommend using [Throwable](https://metacpan.org/pod/Throwable) instead of this module. + +**RECOMMENDATION 2**: Whether or not you use [Throwable](https://metacpan.org/pod/Throwable), you should use +[Try::Tiny](https://metacpan.org/pod/Try::Tiny). + +Exception::Class allows you to declare exception hierarchies in your modules +in a "Java-esque" manner. + +It features a simple interface allowing programmers to 'declare' exception +classes at compile time. It also has a base exception class, +[Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base), that can be easily extended. + +It is designed to make structured exception handling simpler and better by +encouraging people to use hierarchies of exceptions in their applications, as +opposed to a single catch-all exception class. + +This module does not implement any try/catch syntax. Please see the "OTHER +EXCEPTION MODULES (try/catch syntax)" section for more information on how to +get this syntax. + +You will also want to look at the documentation for [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base), +which is the default base class for all exception objects created by this +module. + +# DECLARING EXCEPTION CLASSES + +Importing `Exception::Class` allows you to automagically create +[Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) subclasses. You can also create subclasses via the +traditional means of defining your own subclass with `@ISA`. These two +methods may be easily combined, so that you could subclass an exception class +defined via the automagic import, if you desired this. + +The syntax for the magic declarations is as follows: + + 'MANDATORY CLASS NAME' => \%optional_hashref + +The hashref may contain the following options: + +- isa + + This is the class's parent class. If this isn't provided then the class name + in `$Exception::Class::BASE_EXC_CLASS` is assumed to be the parent (see + below). + + This parameter lets you create arbitrarily deep class hierarchies. This can + be any other [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) subclass in your declaration _or_ a + subclass loaded from a module. + + To change the default exception class you will need to change the value of + `$Exception::Class::BASE_EXC_CLASS` _before_ calling `import`. To do this + simply do something like this: + + BEGIN { $Exception::Class::BASE_EXC_CLASS = 'SomeExceptionClass'; } + + If anyone can come up with a more elegant way to do this please let me know. + + CAVEAT: If you want to automagically subclass an [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) + subclass loaded from a file, then you _must_ compile the class (via use or + require or some other magic) _before_ you import `Exception::Class` or + you'll get a compile time error. + +- fields + + This allows you to define additional attributes for your exception class. Any + field you define can be passed to the `throw` or `new` methods as additional + parameters for the constructor. In addition, your exception object will have + an accessor method for the fields you define. + + This parameter can be either a scalar (for a single field) or an array + reference if you need to define multiple fields. + + Fields will be inherited by subclasses. + +- alias + + Specifying an alias causes this class to create a subroutine of the specified + name in the _caller's_ namespace. Calling this subroutine is equivalent to + calling `->throw(@_)` for the given exception class. + + Besides convenience, using aliases also allows for additional compile time + checking. If the alias is called _without parentheses_, as in `throw_fields + "an error occurred"`, then Perl checks for the existence of the + `throw_fields` subroutine at compile time. If instead you do `ExceptionWithFields->throw(...)`, then Perl checks the class name at + runtime, meaning that typos may sneak through. + +- description + + Each exception class has a description method that returns a fixed + string. This should describe the exception _class_ (as opposed to any + particular exception object). This may be useful for debugging if you start + catching exceptions you weren't expecting (particularly if someone forgot to + document them) and you don't understand the error messages. + +The `Exception::Class` magic attempts to detect circular class hierarchies +and will die if it finds one. It also detects missing links in a chain, for +example if you declare Bar to be a subclass of Foo and never declare Foo. + +# [Try::Tiny](https://metacpan.org/pod/Try::Tiny) + +If you are interested in adding try/catch/finally syntactic sugar to your code +then I recommend you check out [Try::Tiny](https://metacpan.org/pod/Try::Tiny). This is a great module that helps +you ignore some of the weirdness with `eval` and `$@`. Here's an example of +how the two modules work together: + + use Exception::Class ( 'My::Exception' ); + use Scalar::Util qw( blessed ); + use Try::Tiny; + + try { + might_throw(); + } + catch { + if ( blessed $_ && $_->isa('My::Exception') ) { + handle_it(); + } + else { + die $_; + } + }; + +Note that you **cannot** use `Exception::Class->caught` with [Try::Tiny](https://metacpan.org/pod/Try::Tiny). + +# Catching Exceptions Without [Try::Tiny](https://metacpan.org/pod/Try::Tiny) + +`Exception::Class` provides some syntactic sugar for catching exceptions in a +safe manner: + + eval {...}; + + if ( my $e = Exception::Class->caught('My::Error') ) { + cleanup(); + do_something_with_exception($e); + } + +The `caught` method takes a class name and returns an exception object if the +last thrown exception is of the given class, or a subclass of that class. If +it is not given any arguments, it simply returns `$@`. + +You should **always** make a copy of the exception object, rather than using +`$@` directly. This is necessary because if your `cleanup` function uses +`eval`, or calls something which uses it, then `$@` is overwritten. Copying +the exception preserves it for the call to `do_something_with_exception`. + +Exception objects also provide a caught method so you can write: + + if ( my $e = My::Error->caught ) { + cleanup(); + do_something_with_exception($e); + } + +## Uncatchable Exceptions + +Internally, the `caught` method will call `isa` on the exception object. You +could make an exception "uncatchable" by overriding `isa` in that class like +this: + + package Exception::Uncatchable; + + sub isa { shift->rethrow } + +Of course, this only works if you always call `Exception::Class->caught` +after an `eval`. + +# USAGE RECOMMENDATION + +If you're creating a complex system that throws lots of different types of +exceptions, consider putting all the exception declarations in one place. For +an app called Foo you might make a `Foo::Exceptions` module and use that in +all your code. This module could just contain the code to make +`Exception::Class` do its automagic class creation. Doing this allows you to +more easily see what exceptions you have, and makes it easier to keep track of +them. + +This might look something like this: + + package Foo::Bar::Exceptions; + + use Exception::Class ( + Foo::Bar::Exception::Senses => + { description => 'sense-related exception' }, + + Foo::Bar::Exception::Smell => { + isa => 'Foo::Bar::Exception::Senses', + fields => 'odor', + description => 'stinky!' + }, + + Foo::Bar::Exception::Taste => { + isa => 'Foo::Bar::Exception::Senses', + fields => [ 'taste', 'bitterness' ], + description => 'like, gag me with a spoon!' + }, + + ... + ); + +You may want to create a real module to subclass [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base) as +well, particularly if you want your exceptions to have more methods. + +## Subclassing Exception::Class::Base + +As part of your usage of `Exception::Class`, you may want to create your own +base exception class which subclasses [Exception::Class::Base](https://metacpan.org/pod/Exception::Class::Base). You should +feel free to subclass any of the methods documented above. For example, you +may want to subclass `new` to add additional information to your exception +objects. + +# Exception::Class FUNCTIONS + +The `Exception::Class` method offers one function, `Classes`, which is not +exported. This method returns a list of the classes that have been created by +calling the `Exception::Class` `import` method. Note that this is _all_ +the subclasses that have been created, so it may include subclasses created by +things like CPAN modules, etc. Also note that if you simply define a subclass +via the normal Perl method of setting `@ISA` or `use base`, then your +subclass will not be included. + +# SUPPORT + +Bugs may be submitted at [https://github.com/houseabsolute/Exception-Class/issues](https://github.com/houseabsolute/Exception-Class/issues). + +I am also usually active on IRC as 'autarch' on `irc://irc.perl.org`. + +# SOURCE + +The source code repository for Exception-Class can be found at [https://github.com/houseabsolute/Exception-Class](https://github.com/houseabsolute/Exception-Class). + +# DONATIONS + +If you'd like to thank me for the work I've done on this module, please +consider making a "donation" to me via PayPal. I spend a lot of free time +creating free software, and would appreciate any support you'd care to offer. + +Please note that **I am not suggesting that you must do this** in order for me +to continue working on this particular software. I will continue to do so, +inasmuch as I have in the past, for as long as it interests me. + +Similarly, a donation made in this way will probably not make me work on this +software much more, unless I get so many donations that I can consider working +on free software full time (let's all have a chuckle at that together). + +To donate, log into PayPal and send money to autarch@urth.org, or use the +button at [http://www.urth.org/~autarch/fs-donation.html](http://www.urth.org/~autarch/fs-donation.html). + +# AUTHOR + +Dave Rolsky + +# CONTRIBUTORS + +- Alexander Batyrshin <0x62ash@gmail.com> +- Leon Timmermans +- Ricardo Signes + +# COPYRIGHT AND LICENSE + +This software is copyright (c) 2017 by Dave Rolsky. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +The full text of the license can be found in the +`LICENSE` file included with this distribution. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..a7293a1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,17 @@ +--- +skip_tags: true +cache: + - C:\strawberry +install: + - if not exist "C:\strawberry" cinst strawberryperl -y + - set PATH=C:\strawberry\perl\bin;C:\strawberry\perl\site\bin;C:\strawberry\c\bin;%PATH% + - cd %APPVEYOR_BUILD_FOLDER% + - cpanm --installdeps . -n +build_script: + - perl -e 1 +test_script: + - prove -lrv t/ +### __app_cisetup__ +# --- {} + +### __app_cisetup__ diff --git a/bench/simple b/bench/simple new file mode 100644 index 0000000..8a5152f --- /dev/null +++ b/bench/simple @@ -0,0 +1,24 @@ +use strict; +use warnings; + +use Moose; +use Benchmark qw( timethese ); + +use Exception::Class ( + 'Ex1', + Ex2 => { isa => 'Ex1' }, +); + +timethese( + 10_000, { + 'one arg' => sub { + eval { Ex1->throw('foo') }; + }, + 'named error arg' => sub { + eval { Ex1->throw( error => 'foo' ) }; + }, + 'several args' => sub { + eval { Ex1->throw( message => 'foo', ignore_class => 'Bar' ) }; + }, + } +); diff --git a/cpanfile b/cpanfile new file mode 100644 index 0000000..1b48d58 --- /dev/null +++ b/cpanfile @@ -0,0 +1,49 @@ +requires "Class::Data::Inheritable" => "0.02"; +requires "Devel::StackTrace" => "2.00"; +requires "Scalar::Util" => "0"; +requires "base" => "0"; +requires "overload" => "0"; +requires "perl" => "5.008001"; +requires "strict" => "0"; +requires "warnings" => "0"; + +on 'test' => sub { + requires "ExtUtils::MakeMaker" => "0"; + requires "File::Spec" => "0"; + requires "Test::More" => "0.96"; +}; + +on 'test' => sub { + recommends "CPAN::Meta" => "2.120900"; +}; + +on 'configure' => sub { + requires "ExtUtils::MakeMaker" => "0"; +}; + +on 'develop' => sub { + requires "Code::TidyAll" => "0.56"; + requires "Code::TidyAll::Plugin::SortLines::Naturally" => "0.000003"; + requires "Code::TidyAll::Plugin::Test::Vars" => "0.02"; + requires "File::Spec" => "0"; + requires "IO::Handle" => "0"; + requires "IPC::Open3" => "0"; + requires "Parallel::ForkManager" => "1.19"; + requires "Perl::Critic" => "1.126"; + requires "Perl::Tidy" => "20160302"; + requires "Pod::Coverage::TrustPod" => "0"; + requires "Pod::Wordlist" => "0"; + requires "Test::CPAN::Changes" => "0.19"; + requires "Test::CPAN::Meta::JSON" => "0.16"; + requires "Test::Code::TidyAll" => "0.50"; + requires "Test::EOL" => "0"; + requires "Test::Mojibake" => "0"; + requires "Test::More" => "0.96"; + requires "Test::NoTabs" => "0"; + requires "Test::Pod" => "1.41"; + requires "Test::Pod::Coverage" => "1.08"; + requires "Test::Portability::Files" => "0"; + requires "Test::Spelling" => "0.12"; + requires "Test::Vars" => "0.009"; + requires "Test::Version" => "2.05"; +}; diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..32ebf8a --- /dev/null +++ b/dist.ini @@ -0,0 +1,30 @@ +name = Exception-Class +author = Dave Rolsky +license = Perl_5 +copyright_holder = Dave Rolsky + +[@DROLSKY] +dist = Exception-Class +next_release_width = 7 +stopwords = Arun +stopwords = CPAN +stopwords = Kumar's +stopwords = PayPal +stopwords = Rolsky +stopwords = Uncatchable +stopwords = automagic +stopwords = egid +stopwords = esque +stopwords = isa +stopwords = namespace +stopwords = param +stopwords = refcount +stopwords = rethrow +stopwords = runtime +stopwords = stacktrace +stopwords = stringifies +stopwords = subclasses +stopwords = uncatchable +use_github_issues = 1 +-remove = Test::CleanNamespaces +-remove = Test::Synopsis diff --git a/lib/Exception/Class.pm b/lib/Exception/Class.pm new file mode 100644 index 0000000..3d1a5a8 --- /dev/null +++ b/lib/Exception/Class.pm @@ -0,0 +1,569 @@ +package Exception::Class; + +use 5.008001; + +use strict; +use warnings; + +our $VERSION = '1.44'; + +use Exception::Class::Base; +use Scalar::Util qw( blessed reftype ); + +our $BASE_EXC_CLASS; +BEGIN { $BASE_EXC_CLASS ||= 'Exception::Class::Base'; } + +our %CLASSES; + +sub import { + my $class = shift; + + ## no critic (Variables::ProhibitPackageVars) + local $Exception::Class::Caller = caller(); + + my %c; + + my %needs_parent; + while ( my $subclass = shift ) { + my $def = ref $_[0] ? shift : {}; + $def->{isa} + = $def->{isa} + ? ( ref $def->{isa} ? $def->{isa} : [ $def->{isa} ] ) + : []; + + $c{$subclass} = $def; + } + + # We need to sort by length because if we check for keys in the + # Foo::Bar:: stash, this creates a "Bar::" key in the Foo:: stash! +MAKE_CLASSES: + foreach my $subclass ( sort { length $a <=> length $b } keys %c ) { + my $def = $c{$subclass}; + + # We already made this one. + next if $CLASSES{$subclass}; + + { + ## no critic (TestingAndDebugging::ProhibitNoStrict) + no strict 'refs'; + foreach my $parent ( @{ $def->{isa} } ) { + unless ( keys %{"$parent\::"} ) { + $needs_parent{$subclass} = { + parents => $def->{isa}, + def => $def + }; + next MAKE_CLASSES; + } + } + } + + $class->_make_subclass( + subclass => $subclass, + def => $def || {}, + ); + } + + foreach my $subclass ( keys %needs_parent ) { + + # This will be used to spot circular references. + my %seen; + $class->_make_parents( \%needs_parent, $subclass, \%seen ); + } +} + +sub _make_parents { + my $class = shift; + my $needs = shift; + my $subclass = shift; + my $seen = shift; + my $child = shift; # Just for error messages. + + ## no critic (TestingAndDebugging::ProhibitNoStrict, TestingAndDebugging::ProhibitProlongedStrictureOverride) + no strict 'refs'; + + # What if someone makes a typo in specifying their 'isa' param? + # This should catch it. Either it's been made because it didn't + # have missing parents OR it's in our hash as needing a parent. + # If neither of these is true then the _only_ place it is + # mentioned is in the 'isa' param for some other class, which is + # not a good enough reason to make a new class. + die + "Class $subclass appears to be a typo as it is only specified in the 'isa' param for $child\n" + unless exists $needs->{$subclass} + || $CLASSES{$subclass} + || keys %{"$subclass\::"}; + + foreach my $c ( @{ $needs->{$subclass}{parents} } ) { + + # It's been made + next if $CLASSES{$c} || keys %{"$c\::"}; + + die "There appears to be some circularity involving $subclass\n" + if $seen->{$subclass}; + + $seen->{$subclass} = 1; + + $class->_make_parents( $needs, $c, $seen, $subclass ); + } + + return if $CLASSES{$subclass} || keys %{"$subclass\::"}; + + $class->_make_subclass( + subclass => $subclass, + def => $needs->{$subclass}{def} + ); +} + +sub _make_subclass { + my $class = shift; + my %p = @_; + + my $subclass = $p{subclass}; + my $def = $p{def}; + + my $isa; + if ( $def->{isa} ) { + $isa = ref $def->{isa} ? join q{ }, @{ $def->{isa} } : $def->{isa}; + } + $isa ||= $BASE_EXC_CLASS; + + my $version_name = 'VERSION'; + + my $code = <<"EOPERL"; +package $subclass; + +use base qw($isa); + +our \$$version_name = '1.1'; + +1; + +EOPERL + + if ( $def->{description} ) { + ( my $desc = $def->{description} ) =~ s/([\\\'])/\\$1/g; + $code .= <<"EOPERL"; +sub description +{ + return '$desc'; +} +EOPERL + } + + my @fields; + if ( my $fields = $def->{fields} ) { + @fields + = ref $fields && reftype $fields eq 'ARRAY' ? @$fields : $fields; + + $code + .= 'sub Fields { return ($_[0]->SUPER::Fields, ' + . join( ', ', map {"'$_'"} @fields ) + . ") }\n\n"; + + foreach my $field (@fields) { + $code .= sprintf( "sub %s { \$_[0]->{%s} }\n", $field, $field ); + } + } + + if ( my $alias = $def->{alias} ) { + ## no critic (Variables::ProhibitPackageVars) + die 'Cannot make alias without caller' + unless defined $Exception::Class::Caller; + + ## no critic (TestingAndDebugging::ProhibitNoStrict) + no strict 'refs'; + *{"$Exception::Class::Caller\::$alias"} + = sub { $subclass->throw(@_) }; + } + + if ( my $defaults = $def->{defaults} ) { + $code + .= "sub _defaults { return shift->SUPER::_defaults, our \%_DEFAULTS }\n"; + ## no critic (TestingAndDebugging::ProhibitNoStrict) + no strict 'refs'; + *{"$subclass\::_DEFAULTS"} = {%$defaults}; + } + + ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval) + eval $code; + die $@ if $@; + + ( my $filename = "$subclass.pm" ) =~ s{::}{/}g; + $INC{$filename} = __FILE__; + + $CLASSES{$subclass} = 1; +} + +sub caught { + my $e = $@; + + return $e unless $_[1]; + + return unless blessed($e) && $e->isa( $_[1] ); + return $e; +} + +sub Classes { sort keys %Exception::Class::CLASSES } + +1; + +# ABSTRACT: A module that allows you to declare real exception classes in Perl + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Exception::Class - A module that allows you to declare real exception classes in Perl + +=head1 VERSION + +version 1.44 + +=head1 SYNOPSIS + + use Exception::Class ( + 'MyException', + + 'AnotherException' => { isa => 'MyException' }, + + 'YetAnotherException' => { + isa => 'AnotherException', + description => 'These exceptions are related to IPC' + }, + + 'ExceptionWithFields' => { + isa => 'YetAnotherException', + fields => [ 'grandiosity', 'quixotic' ], + alias => 'throw_fields', + }, + ); + use Scalar::Util qw( blessed ); + use Try::Tiny; + + try { + MyException->throw( error => 'I feel funny.' ); + } + catch { + die $_ unless blessed $_ && $_->can('rethrow'); + + if ( $_->isa('Exception::Class') ) { + warn $_->error, "\n", $_->trace->as_string, "\n"; + warn join ' ', $_->euid, $_->egid, $_->uid, $_->gid, $_->pid, $_->time; + + exit; + } + elsif ( $_->isa('ExceptionWithFields') ) { + if ( $_->quixotic ) { + handle_quixotic_exception(); + } + else { + handle_non_quixotic_exception(); + } + } + else { + $_->rethrow; + } + }; + + # without Try::Tiny + eval { ... }; + if ( my $e = Exception::Class->caught ) { ... } + + # use an alias - without parens subroutine name is checked at + # compile time + throw_fields error => "No strawberry", grandiosity => "quite a bit"; + +=head1 DESCRIPTION + +B: If you are writing modern Perl code with L or +L I highly recommend using L instead of this module. + +B: Whether or not you use L, you should use +L. + +Exception::Class allows you to declare exception hierarchies in your modules +in a "Java-esque" manner. + +It features a simple interface allowing programmers to 'declare' exception +classes at compile time. It also has a base exception class, +L, that can be easily extended. + +It is designed to make structured exception handling simpler and better by +encouraging people to use hierarchies of exceptions in their applications, as +opposed to a single catch-all exception class. + +This module does not implement any try/catch syntax. Please see the "OTHER +EXCEPTION MODULES (try/catch syntax)" section for more information on how to +get this syntax. + +You will also want to look at the documentation for L, +which is the default base class for all exception objects created by this +module. + +=for Pod::Coverage Classes + caught + +=head1 DECLARING EXCEPTION CLASSES + +Importing C allows you to automagically create +L subclasses. You can also create subclasses via the +traditional means of defining your own subclass with C<@ISA>. These two +methods may be easily combined, so that you could subclass an exception class +defined via the automagic import, if you desired this. + +The syntax for the magic declarations is as follows: + + 'MANDATORY CLASS NAME' => \%optional_hashref + +The hashref may contain the following options: + +=over 4 + +=item * isa + +This is the class's parent class. If this isn't provided then the class name +in C<$Exception::Class::BASE_EXC_CLASS> is assumed to be the parent (see +below). + +This parameter lets you create arbitrarily deep class hierarchies. This can +be any other L subclass in your declaration I a +subclass loaded from a module. + +To change the default exception class you will need to change the value of +C<$Exception::Class::BASE_EXC_CLASS> I calling C. To do this +simply do something like this: + + BEGIN { $Exception::Class::BASE_EXC_CLASS = 'SomeExceptionClass'; } + +If anyone can come up with a more elegant way to do this please let me know. + +CAVEAT: If you want to automagically subclass an L +subclass loaded from a file, then you I compile the class (via use or +require or some other magic) I you import C or +you'll get a compile time error. + +=item * fields + +This allows you to define additional attributes for your exception class. Any +field you define can be passed to the C or C methods as additional +parameters for the constructor. In addition, your exception object will have +an accessor method for the fields you define. + +This parameter can be either a scalar (for a single field) or an array +reference if you need to define multiple fields. + +Fields will be inherited by subclasses. + +=item * alias + +Specifying an alias causes this class to create a subroutine of the specified +name in the I namespace. Calling this subroutine is equivalent to +calling C<< ->throw(@_) >> for the given exception class. + +Besides convenience, using aliases also allows for additional compile time +checking. If the alias is called I, as in C, then Perl checks for the existence of the +C subroutine at compile time. If instead you do C<< +ExceptionWithFields->throw(...) >>, then Perl checks the class name at +runtime, meaning that typos may sneak through. + +=item * description + +Each exception class has a description method that returns a fixed +string. This should describe the exception I (as opposed to any +particular exception object). This may be useful for debugging if you start +catching exceptions you weren't expecting (particularly if someone forgot to +document them) and you don't understand the error messages. + +=back + +The C magic attempts to detect circular class hierarchies +and will die if it finds one. It also detects missing links in a chain, for +example if you declare Bar to be a subclass of Foo and never declare Foo. + +=head1 L + +If you are interested in adding try/catch/finally syntactic sugar to your code +then I recommend you check out L. This is a great module that helps +you ignore some of the weirdness with C and C<$@>. Here's an example of +how the two modules work together: + + use Exception::Class ( 'My::Exception' ); + use Scalar::Util qw( blessed ); + use Try::Tiny; + + try { + might_throw(); + } + catch { + if ( blessed $_ && $_->isa('My::Exception') ) { + handle_it(); + } + else { + die $_; + } + }; + +Note that you B use C<< Exception::Class->caught >> with L. + +=head1 Catching Exceptions Without L + +C provides some syntactic sugar for catching exceptions in a +safe manner: + + eval {...}; + + if ( my $e = Exception::Class->caught('My::Error') ) { + cleanup(); + do_something_with_exception($e); + } + +The C method takes a class name and returns an exception object if the +last thrown exception is of the given class, or a subclass of that class. If +it is not given any arguments, it simply returns C<$@>. + +You should B make a copy of the exception object, rather than using +C<$@> directly. This is necessary because if your C function uses +C, or calls something which uses it, then C<$@> is overwritten. Copying +the exception preserves it for the call to C. + +Exception objects also provide a caught method so you can write: + + if ( my $e = My::Error->caught ) { + cleanup(); + do_something_with_exception($e); + } + +=head2 Uncatchable Exceptions + +Internally, the C method will call C on the exception object. You +could make an exception "uncatchable" by overriding C in that class like +this: + + package Exception::Uncatchable; + + sub isa { shift->rethrow } + +Of course, this only works if you always call C<< Exception::Class->caught >> +after an C. + +=head1 USAGE RECOMMENDATION + +If you're creating a complex system that throws lots of different types of +exceptions, consider putting all the exception declarations in one place. For +an app called Foo you might make a C module and use that in +all your code. This module could just contain the code to make +C do its automagic class creation. Doing this allows you to +more easily see what exceptions you have, and makes it easier to keep track of +them. + +This might look something like this: + + package Foo::Bar::Exceptions; + + use Exception::Class ( + Foo::Bar::Exception::Senses => + { description => 'sense-related exception' }, + + Foo::Bar::Exception::Smell => { + isa => 'Foo::Bar::Exception::Senses', + fields => 'odor', + description => 'stinky!' + }, + + Foo::Bar::Exception::Taste => { + isa => 'Foo::Bar::Exception::Senses', + fields => [ 'taste', 'bitterness' ], + description => 'like, gag me with a spoon!' + }, + + ... + ); + +You may want to create a real module to subclass L as +well, particularly if you want your exceptions to have more methods. + +=head2 Subclassing Exception::Class::Base + +As part of your usage of C, you may want to create your own +base exception class which subclasses L. You should +feel free to subclass any of the methods documented above. For example, you +may want to subclass C to add additional information to your exception +objects. + +=head1 Exception::Class FUNCTIONS + +The C method offers one function, C, which is not +exported. This method returns a list of the classes that have been created by +calling the C C method. Note that this is I +the subclasses that have been created, so it may include subclasses created by +things like CPAN modules, etc. Also note that if you simply define a subclass +via the normal Perl method of setting C<@ISA> or C, then your +subclass will not be included. + +=head1 SUPPORT + +Bugs may be submitted at L. + +I am also usually active on IRC as 'autarch' on C. + +=head1 SOURCE + +The source code repository for Exception-Class can be found at L. + +=head1 DONATIONS + +If you'd like to thank me for the work I've done on this module, please +consider making a "donation" to me via PayPal. I spend a lot of free time +creating free software, and would appreciate any support you'd care to offer. + +Please note that B in order for me +to continue working on this particular software. I will continue to do so, +inasmuch as I have in the past, for as long as it interests me. + +Similarly, a donation made in this way will probably not make me work on this +software much more, unless I get so many donations that I can consider working +on free software full time (let's all have a chuckle at that together). + +To donate, log into PayPal and send money to autarch@urth.org, or use the +button at L. + +=head1 AUTHOR + +Dave Rolsky + +=head1 CONTRIBUTORS + +=for stopwords Alexander Batyrshin Leon Timmermans Ricardo Signes + +=over 4 + +=item * + +Alexander Batyrshin <0x62ash@gmail.com> + +=item * + +Leon Timmermans + +=item * + +Ricardo Signes + +=back + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2017 by Dave Rolsky. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +The full text of the license can be found in the +F file included with this distribution. + +=cut diff --git a/lib/Exception/Class/Base.pm b/lib/Exception/Class/Base.pm new file mode 100644 index 0000000..04d0d5c --- /dev/null +++ b/lib/Exception/Class/Base.pm @@ -0,0 +1,595 @@ +package Exception::Class::Base; + +use strict; +use warnings; + +our $VERSION = '1.44'; + +use Class::Data::Inheritable 0.02; +use Devel::StackTrace 2.00; +use Scalar::Util qw( blessed ); + +use base qw(Class::Data::Inheritable); + +BEGIN { + __PACKAGE__->mk_classdata('Trace'); + __PACKAGE__->mk_classdata('UnsafeRefCapture'); + + __PACKAGE__->mk_classdata('NoContextInfo'); + __PACKAGE__->NoContextInfo(0); + + __PACKAGE__->mk_classdata('RespectOverload'); + __PACKAGE__->RespectOverload(0); + + __PACKAGE__->mk_classdata('MaxArgLength'); + __PACKAGE__->MaxArgLength(0); + + sub NoRefs { + my $self = shift; + if (@_) { + my $val = shift; + return $self->UnsafeRefCapture( !$val ); + } + else { + return $self->UnsafeRefCapture; + } + } + + sub Fields { () } +} + +use overload + + # an exception is always true + bool => sub {1}, '""' => 'as_string', fallback => 1; + +# Create accessor routines +BEGIN { + my @fields = qw( message pid uid euid gid egid time trace ); + + foreach my $f (@fields) { + my $sub = sub { my $s = shift; return $s->{$f}; }; + + ## no critic (TestingAndDebugging::ProhibitNoStrict) + no strict 'refs'; + *{$f} = $sub; + } + *error = \&message; + + my %trace_fields = ( + package => 'package', + file => 'filename', + line => 'line', + ); + + while ( my ( $f, $m ) = each %trace_fields ) { + my $sub = sub { + my $s = shift; + return $s->{$f} if exists $s->{$f}; + + my $frame = $s->trace->frame(0); + + return $s->{$f} = $frame ? $frame->$m : undef; + }; + + ## no critic (TestingAndDebugging::ProhibitNoStrict) + no strict 'refs'; + *{$f} = $sub; + } +} + +sub Classes { Exception::Class::Classes() } + +sub throw { + my $proto = shift; + + $proto->rethrow if ref $proto; + + die $proto->new(@_); +} + +sub rethrow { + my $self = shift; + + die $self; +} + +sub new { + my $proto = shift; + my $class = ref $proto || $proto; + + my $self = bless {}, $class; + + $self->_initialize(@_); + + return $self; +} + +sub _initialize { + my $self = shift; + my %p = @_ == 1 ? ( error => $_[0] ) : @_; + + $self->{message} = $p{message} || $p{error} || q{}; + + $self->{show_trace} = $p{show_trace} if exists $p{show_trace}; + + if ( $self->NoContextInfo ) { + $self->{show_trace} = 0; + $self->{package} = $self->{file} = $self->{line} = undef; + } + else { + # CORE::time is important to fix an error with some versions of + # Perl + $self->{time} = CORE::time(); + $self->{pid} = $$; + $self->{uid} = $<; + $self->{euid} = $>; + $self->{gid} = $(; + $self->{egid} = $); + + my @ignore_class = (__PACKAGE__); + my @ignore_package = 'Exception::Class'; + + if ( my $i = delete $p{ignore_class} ) { + push @ignore_class, ( ref($i) eq 'ARRAY' ? @$i : $i ); + } + + if ( my $i = delete $p{ignore_package} ) { + push @ignore_package, ( ref($i) eq 'ARRAY' ? @$i : $i ); + } + + $self->{trace} = Devel::StackTrace->new( + ignore_class => \@ignore_class, + ignore_package => \@ignore_package, + unsafe_ref_capture => $self->UnsafeRefCapture, + respect_overload => $self->RespectOverload, + max_arg_length => $self->MaxArgLength, + map { $p{$_} ? ( $_ => delete $p{$_} ) : () } qw( + frame_filter + filter_frames_early + skip_frames + ), + ); + } + + my %fields = map { $_ => 1 } $self->Fields; + while ( my ( $key, $value ) = each %p ) { + next if $key =~ /^(?:error|message|show_trace)$/; + + if ( $fields{$key} ) { + $self->{$key} = $value; + } + else { + Exception::Class::Base->throw( + error => "unknown field $key passed to constructor for class " + . ref $self ); + } + } +} + +sub context_hash { + my $self = shift; + + return { + time => $self->{time}, + pid => $self->{pid}, + uid => $self->{uid}, + euid => $self->{euid}, + gid => $self->{gid}, + egid => $self->{egid}, + }; +} + +sub field_hash { + my $self = shift; + + my $hash = {}; + + for my $field ( $self->Fields ) { + $hash->{$field} = $self->$field; + } + + return $hash; +} + +sub description { + return 'Generic exception'; +} + +sub show_trace { + my $self = shift; + + return 0 unless $self->{trace}; + + if (@_) { + $self->{show_trace} = shift; + } + + return exists $self->{show_trace} ? $self->{show_trace} : $self->Trace; +} + +sub as_string { + my $self = shift; + + my $str = $self->full_message; + unless ( defined $str && length $str ) { + my $desc = $self->description; + $str = defined $desc + && length $desc ? "[$desc]" : '[Generic exception]'; + } + + $str .= "\n\n" . $self->trace->as_string + if $self->show_trace; + + return $str; +} + +sub full_message { $_[0]->message } + +# +# The %seen bit protects against circular inheritance. +# +## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval) +eval <<'EOF' if $] == 5.006; +sub isa { + my ( $inheritor, $base ) = @_; + $inheritor = ref($inheritor) if ref($inheritor); + + my %seen; + + no strict 'refs'; + my @parents = ( $inheritor, @{"$inheritor\::ISA"} ); + while ( my $class = shift @parents ) { + return 1 if $class eq $base; + + push @parents, grep { !$seen{$_}++ } @{"$class\::ISA"}; + } + return 0; +} +EOF + +sub caught { + my $class = shift; + + my $e = $@; + + return unless defined $e && blessed($e) && $e->isa($class); + return $e; +} + +1; + +# ABSTRACT: A base class for exception objects + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Exception::Class::Base - A base class for exception objects + +=head1 VERSION + +version 1.44 + +=head1 SYNOPSIS + + use Exception::Class 'MyException'; + + eval { MyException->throw( error => 'I feel funny.' ) }; + + print $@->error; + +=head1 DESCRIPTION + +This class is the base class for all exceptions created by +L. It provides a number of methods for getting information +about the exception. + +=for Pod::Coverage Classes + caught + NoRefs + +=head1 METHODS + +=head2 MyException->Trace($boolean) + +Each C subclass can be set individually to include a +stacktrace when the C method is called. The default is to not +include a stacktrace. Calling this method with a value changes this +behavior. It always returns the current value (after any change is applied). + +This value is inherited by any subclasses. However, if this value is set for a +subclass, it will thereafter be independent of the value in +C. + +Do not call this on the C class directly or you'll +change it for all exception classes that use L, including +ones created in modules you don't control. + +This is a class method, not an object method. + +=head2 MyException->UnsafeRefCapture($boolean) + +When a C object is created, it walks through the stack and +stores the arguments which were passed to each subroutine on the stack. If any +of these arguments are references, then that means that the +C ends up increasing the ref count of these references, +delaying their destruction. + +Since C uses C internally, this +method provides a way to tell C not to store these +references. Instead, C replaces references with their +stringified representation. + +This method defaults to false. As with C, it is inherited by subclasses +but setting it in a subclass makes it independent thereafter. + +Do not call this on the C class directly or you'll +change it for all exception classes that use L, including +ones created in modules you don't control. + +=head2 MyException->RespectOverload($boolean) + +When a C object stringifies, by default it ignores +stringification overloading on any objects being dealt with. + +Since C uses C internally, this +method provides a way to tell C to respect overloading. + +This method defaults to false. As with C, it is inherited by subclasses +but setting it in a subclass makes it independent thereafter. + +Do not call this on the C class directly or you'll +change it for all exception classes that use L, including +ones created in modules you don't control. + +=head2 MyException->MaxArgLength($boolean) + +When a C object stringifies, by default it displays the +full argument for each function. This parameter can be used to limit the +maximum length of each argument. + +Since C uses C internally, this +method provides a way to tell C to limit the length of +arguments. + +This method defaults to 0. As with C, it is inherited by subclasses but +setting it in a subclass makes it independent thereafter. + +Do not call this on the C class directly or you'll +change it for all exception classes that use L, including +ones created in modules you don't control. + +=head2 MyException->Fields + +This method returns the extra fields defined for the given class, as a list. + +Do not call this on the C class directly or you'll +change it for all exception classes that use L, including +ones created in modules you don't control. + +=head2 MyException->throw( $message ) + +=head2 MyException->throw( message => $message ) + +=head2 MyException->throw( error => $error ) + +This method creates a new object with the given error message. If no error +message is given, this will be an empty string. It then dies with this object +as its argument. + +This method also takes a C parameter which indicates whether or +not the particular exception object being created should show a stacktrace +when its C method is called. This overrides the value of C +for this class if it is given. + +The frames included in the trace can be controlled by the C and +C parameters. These are passed directly to Devel::Stacktrace's +constructor. See C for more details. This class B +passes C<__PACKAGE__> for C and C<'Exception::Class'> for +C, in addition to any arguments you provide. + +If only a single value is given to the constructor it is assumed to be the +message parameter. + +Additional keys corresponding to the fields defined for the particular +exception subclass will also be accepted. + +=head2 MyException->new(...) + +This method takes the same parameters as C, but instead of dying simply +returns a new exception object. + +This method is always called when constructing a new exception object via the +C method. + +=head2 MyException->description + +Returns the description for the given C subclass. The +C class's description is "Generic exception" (this may +change in the future). This is also an object method. + +=head2 $exception->rethrow + +Simply dies with the object as its sole argument. It's just syntactic +sugar. This does not change any of the object's attribute values. However, it +will cause C to report the die as coming from within the +C class rather than where rethrow was called. + +Of course, you always have access to the original stacktrace for the exception +object. + +=head2 $exception->message + +=head2 $exception->error + +Returns the error/message associated with the exception. + +=head2 $exception->pid + +Returns the pid at the time the exception was thrown. + +=head2 $exception->uid + +Returns the real user id at the time the exception was thrown. + +=head2 $exception->gid + +Returns the real group id at the time the exception was thrown. + +=head2 $exception->euid + +Returns the effective user id at the time the exception was thrown. + +=head2 $exception->egid + +Returns the effective group id at the time the exception was thrown. + +=head2 $exception->time + +Returns the time in seconds since the epoch at the time the exception was +thrown. + +=head2 $exception->package + +Returns the package from which the exception was thrown. + +=head2 $exception->file + +Returns the file within which the exception was thrown. + +=head2 $exception->line + +Returns the line where the exception was thrown. + +=head2 $exception->context_hash + +Returns a hash reference with the following keys: + +=over 4 + +=item * time + +=item * pid + +=item * uid + +=item * euid + +=item * gid + +=item * egid + +=back + +=head2 $exception->field_hash + +Returns a hash reference where the keys are any fields defined for the +exception class and the values are the values associated with the field in the +given object. + +=head2 $exception->trace + +Returns the trace object associated with the object. + +=head2 $exception->show_trace($boolean) + +This method can be used to set whether or not a stack trace is included when +the as_string method is called or the object is stringified. + +=head2 $exception->as_string + +Returns a string form of the error message (something like what you'd expect +from die). If the class or object is set to show traces then then the full +trace is also included. The result looks like C. + +=head2 $exception->full_message + +Called by the C method to get the message. By default, this is the +same as calling the C method, but may be overridden by a +subclass. See below for details. + +=head1 LIGHTWEIGHT EXCEPTIONS + +A lightweight exception is one which records no information about its context +when it is created. This can be achieved by setting C<< $class->NoContextInfo +>> to a true value. + +You can make this the default for a class of exceptions by setting it after +creating the class: + + use Exception::Class ( + 'LightWeight', + 'HeavyWeight', + ); + + LightWeight->NoContextInfo(1); + +A lightweight exception does have a stack trace object, nor does it record the +time, pid, uid, euid, gid, or egid. It only has a message. + +=head1 OVERLOADING + +C objects are overloaded so that stringification +produces a normal error message. This just calls the C<< $exception->as_string +>> method described above. This means that you can just C after an +C and not worry about whether or not its an actual object. It also means +an application or module could do this: + + $SIG{__DIE__} = sub { Exception::Class::Base->throw( error => join '', @_ ); }; + +and this would probably not break anything (unless someone was expecting a +different type of exception object from C). + +=head1 OVERRIDING THE as_string METHOD + +By default, the C method simply returns the value C or +C param plus a stack trace, if the class's C method returns a +true value or C was set when creating the exception. + +However, once you add new fields to a subclass, you may want to include those +fields in the stringified error. + +Inside the C method, the message (non-stack trace) portion of the +error is generated by calling the C method. This can be easily +overridden. For example: + + sub full_message { + my $self = shift; + + my $msg = $self->message; + + $msg .= " and foo was " . $self->foo; + + return $msg; + } + +=head1 SUPPORT + +Bugs may be submitted at L. + +I am also usually active on IRC as 'autarch' on C. + +=head1 SOURCE + +The source code repository for Exception-Class can be found at L. + +=head1 AUTHOR + +Dave Rolsky + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2017 by Dave Rolsky. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +The full text of the license can be found in the +F file included with this distribution. + +=cut diff --git a/perlcriticrc b/perlcriticrc new file mode 100644 index 0000000..3dc01f3 --- /dev/null +++ b/perlcriticrc @@ -0,0 +1,70 @@ +severity = 3 +verbose = 11 +theme = (core && (pbp || bugs || maintenance || cosmetic || complexity || security || tests)) || moose +program-extensions = pl psgi t + +exclude = Subroutines::ProhibitCallsToUndeclaredSubs + +[BuiltinFunctions::ProhibitStringySplit] +severity = 3 + +[CodeLayout::RequireTrailingCommas] +severity = 3 + +[ControlStructures::ProhibitCStyleForLoops] +severity = 3 + +[InputOutput::RequireCheckedSyscalls] +functions = :builtins +exclude_functions = sleep +severity = 3 + +[RegularExpressions::ProhibitComplexRegexes] +max_characters = 200 + +[RegularExpressions::ProhibitUnusualDelimiters] +severity = 3 + +[Subroutines::ProhibitUnusedPrivateSubroutines] +private_name_regex = _(?!build)\w+ + +[TestingAndDebugging::ProhibitNoWarnings] +allow = redefine + +[ValuesAndExpressions::ProhibitEmptyQuotes] +severity = 3 + +[ValuesAndExpressions::ProhibitInterpolationOfLiterals] +severity = 3 + +[ValuesAndExpressions::RequireUpperCaseHeredocTerminator] +severity = 3 + +[Variables::ProhibitPackageVars] +add_packages = Carp Test::Builder + +[-Subroutines::RequireFinalReturn] + +# This incorrectly thinks signatures are prototypes. +[-Subroutines::ProhibitSubroutinePrototypes] + +[-ErrorHandling::RequireCarping] + +# No need for /xsm everywhere +[-RegularExpressions::RequireDotMatchAnything] +[-RegularExpressions::RequireExtendedFormatting] +[-RegularExpressions::RequireLineBoundaryMatching] + +# http://stackoverflow.com/questions/2275317/why-does-perlcritic-dislike-using-shift-to-populate-subroutine-variables +[-Subroutines::RequireArgUnpacking] + +# "use v5.14" is more readable than "use 5.014" +[-ValuesAndExpressions::ProhibitVersionStrings] + +# Explicitly returning undef is a _good_ thing in many cases, since it +# prevents very common errors when using a sub in list context to construct a +# hash and ending up with a missing value or key. +[-Subroutines::ProhibitExplicitReturnUndef] + +[Variables::RequireLocalizedPunctuationVars] +allow = %INC diff --git a/perltidyrc b/perltidyrc new file mode 100644 index 0000000..b54e60d --- /dev/null +++ b/perltidyrc @@ -0,0 +1,22 @@ +-l=78 +-i=4 +-ci=4 +-se +-b +-bar +-boc +-vt=0 +-vtc=0 +-cti=0 +-pt=1 +-bt=1 +-sbt=1 +-bbt=1 +-nolq +-npro +-nsfs +--blank-lines-before-packages=0 +--opening-hash-brace-right +--no-outdent-long-comments +--iterations=2 +-wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x=" diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd new file mode 100644 index 0000000..8ab6a5d --- /dev/null +++ b/t/00-report-prereqs.dd @@ -0,0 +1,59 @@ +do { my $x = { + 'configure' => { + 'requires' => { + 'ExtUtils::MakeMaker' => '0' + } + }, + 'develop' => { + 'requires' => { + 'Code::TidyAll' => '0.56', + 'Code::TidyAll::Plugin::SortLines::Naturally' => '0.000003', + 'Code::TidyAll::Plugin::Test::Vars' => '0.02', + 'File::Spec' => '0', + 'IO::Handle' => '0', + 'IPC::Open3' => '0', + 'Parallel::ForkManager' => '1.19', + 'Perl::Critic' => '1.126', + 'Perl::Tidy' => '20160302', + 'Pod::Coverage::TrustPod' => '0', + 'Pod::Wordlist' => '0', + 'Test::CPAN::Changes' => '0.19', + 'Test::CPAN::Meta::JSON' => '0.16', + 'Test::Code::TidyAll' => '0.50', + 'Test::EOL' => '0', + 'Test::Mojibake' => '0', + 'Test::More' => '0.96', + 'Test::NoTabs' => '0', + 'Test::Pod' => '1.41', + 'Test::Pod::Coverage' => '1.08', + 'Test::Portability::Files' => '0', + 'Test::Spelling' => '0.12', + 'Test::Vars' => '0.009', + 'Test::Version' => '2.05' + } + }, + 'runtime' => { + 'requires' => { + 'Class::Data::Inheritable' => '0.02', + 'Devel::StackTrace' => '2.00', + 'Scalar::Util' => '0', + 'base' => '0', + 'overload' => '0', + 'perl' => '5.008001', + 'strict' => '0', + 'warnings' => '0' + } + }, + 'test' => { + 'recommends' => { + 'CPAN::Meta' => '2.120900' + }, + 'requires' => { + 'ExtUtils::MakeMaker' => '0', + 'File::Spec' => '0', + 'Test::More' => '0.96' + } + } + }; + $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..c72183a --- /dev/null +++ b/t/00-report-prereqs.t @@ -0,0 +1,193 @@ +#!perl + +use strict; +use warnings; + +# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.027 + +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'; +my $cpan_meta_error; +if ( $source && $HAS_CPAN_META + && (my $meta = eval { CPAN::Meta->load_file($source) } ) +) { + $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); +} +else { + $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) + $source = 'static metadata'; +} + +my @full_reports; +my @dep_errors; +my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; + +# Add static includes into a fake section +for my $mod (@include) { + $req_hash->{other}{modules}{$mod} = 0; +} + +for my $phase ( qw(configure build test runtime develop other) ) { + next unless $req_hash->{$phase}; + next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); + + for my $type ( qw(requires recommends suggests conflicts modules) ) { + next unless $req_hash->{$phase}{$type}; + + my $title = ucfirst($phase).' '.ucfirst($type); + my @reports = [qw/Module Want Have/]; + + for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { + next if $mod eq 'perl'; + next if grep { $_ eq $mod } @exclude; + + my $file = $mod; + $file =~ s{::}{/}g; + $file .= ".pm"; + my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; + + my $want = $req_hash->{$phase}{$type}{$mod}; + $want = "undef" unless defined $want; + $want = "any" if !$want && $want == 0; + + my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; + + if ($prefix) { + my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); + $have = "undef" unless defined $have; + push @reports, [$mod, $want, $have]; + + if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { + if ( $have !~ /\A$lax_version_re\z/ ) { + push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; + } + elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { + push @dep_errors, "$mod version '$have' is not in required range '$want'"; + } + } + } + else { + push @reports, [$mod, $want, "missing"]; + + if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { + push @dep_errors, "$mod is not installed ($req_string)"; + } + } + } + + if ( @reports ) { + push @full_reports, "=== $title ===\n\n"; + + my $ml = _max( map { length $_->[0] } @reports ); + my $wl = _max( map { length $_->[1] } @reports ); + my $hl = _max( map { length $_->[2] } @reports ); + + if ($type eq 'modules') { + splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; + push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; + } + else { + splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; + push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; + } + + push @full_reports, "\n"; + } + } +} + +if ( @full_reports ) { + diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; +} + +if ( $cpan_meta_error || @dep_errors ) { + diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; +} + +if ( $cpan_meta_error ) { + my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; + diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; +} + +if ( @dep_errors ) { + diag join("\n", + "\nThe following REQUIRED prerequisites were not satisfied:\n", + @dep_errors, + "\n" + ); +} + +pass; + +# vim: ts=4 sts=4 sw=4 et: diff --git a/t/basic.t b/t/basic.t new file mode 100644 index 0000000..8edc406 --- /dev/null +++ b/t/basic.t @@ -0,0 +1,525 @@ +use strict; +use warnings; + +use File::Spec; + +use Test::More 0.88; + +use Exception::Class; + +# There's actually a few tests here of the import routine. I don't +# really know how to quantify them though. If we fail to compile and +# there's an error from the Exception::Class::Base class then +# something here failed. +BEGIN { + package FooException; + + use Exception::Class; + use base qw(Exception::Class::Base); +} + +use Exception::Class ( + 'YAE' => { isa => 'SubTestException', alias => 'yae' }, + + 'SubTestException' => { + isa => 'TestException', + description => q|blah'\\blah| + }, + + 'TestException', + + 'FooBarException' => { isa => 'FooException' }, + + 'FieldsException' => { isa => 'YAE', fields => [qw( foo bar )] }, + 'MoreFieldsException' => { isa => 'FieldsException', fields => ['yip'] }, + + 'Exc::AsString', + + 'Bool' => { fields => ['something'] }, + + 'ObjectRefs', + 'ObjectRefs2', + 'ObjectRefs3', +); + +$Exception::Class::BASE_EXC_CLASS = 'FooException'; +Exception::Class->import('BlahBlah'); + +## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) + +# Accessors +{ + eval { Exception::Class::Base->throw( error => 'err' ); }; + + my $e = $@; + + isa_ok( $e, 'Exception::Class::Base', '$@' ); + + is( + $e->error, 'err', + q{Exception's error message should be 'err'} + ); + + is( + $e->message, 'err', + q{Exception's message should be 'err'} + ); + + is( + $e->description, 'Generic exception', + q{Description should be 'Generic exception'} + ); + + is( + $e->package, 'main', + q{Package should be 'main'} + ); + + my $expect = File::Spec->catfile( 't', 'basic.t' ); + is( + $e->file, $expect, + "File should be '$expect'" + ); + + is( + $e->line, 52, + 'Line should be 52' + ); + + is( + $e->pid, $$, + "PID should be $$" + ); + + is( + $e->context_hash->{pid}, $$, + 'PID is also in context_hash', + ); + + is( + $e->uid, $<, + "UID should be $<" + ); + + is( + $e->euid, $>, + "EUID should be $>" + ); + + is( + $e->gid, $(, + "GID should be $(" + ); + + is( + $e->egid, $), + "EGID should be $)" + ); + + ok( + defined $e->trace, + 'Exception object should have a stacktrace' + ); +} + +# Test subclass creation +{ + eval { TestException->throw( error => 'err' ); }; + my $e = $@; + + isa_ok( $e, 'TestException' ); + + is( + $e->description, 'Generic exception', + q{Description should be 'Generic exception'} + ); + + eval { SubTestException->throw( error => 'err' ); }; + + $e = $@; + + isa_ok( $e, 'SubTestException' ); + + isa_ok( $e, 'TestException' ); + + isa_ok( $e, 'Exception::Class::Base' ); + + is( + $e->description, q|blah'\\blah|, + q|Description should be "blah'\\blah"| + ); + + eval { YAE->throw( error => 'err' ); }; + + $e = $@; + + isa_ok( $e, 'SubTestException' ); + + eval { BlahBlah->throw( error => 'yadda yadda' ); }; + + $e = $@; + + isa_ok( $e, 'FooException' ); + + isa_ok( $e, 'Exception::Class::Base' ); +} + +# Trace related tests +{ + ok( + !Exception::Class::Base->Trace, + q{Exception::Class::Base class 'Trace' method should return false} + ); + + eval { + Exception::Class::Base->throw( + error => 'has stacktrace', + show_trace => 1, + ); + }; + + my $e = $@; + + like( + $e->as_string, qr/Trace begun/, + 'Setting show_trace to true should override value of Trace' + ); + + Exception::Class::Base->Trace(1); + + ok( + Exception::Class::Base->Trace, + q{Exception::Class::Base class 'Trace' method should return true} + ); + + eval { argh(); }; + + $e = $@; + + ok( + $e->trace->as_string, + 'Exception should have a stack trace' + ); + + eval { + Exception::Class::Base->throw( + error => 'has stacktrace', + show_trace => 0, + ); + }; + + $e = $@; + + unlike( + $e->as_string, qr/Trace begun/, + 'Setting show_trace to false should override value of Trace' + ); + + my @f; + while ( my $f = $e->trace->next_frame ) { push @f, $f; } + + ok( + ( !grep { $_->package eq 'Exception::Class::Base' } @f ), + 'Trace should contain frames from Exception::Class::Base package' + ); +} + +# overloading +{ + Exception::Class::Base->Trace(0); + eval { Exception::Class::Base->throw( error => 'overloaded' ); }; + + my $e = $@; + + is( + "$e", 'overloaded', + 'Overloading in string context' + ); + + Exception::Class::Base->Trace(1); + eval { Exception::Class::Base->throw( error => 'overloaded again' ); }; + +SKIP: + { + skip( 'Perl 5.6.0 is broken. See README.', 1 ) if $] == 5.006; + + my $re = qr/overloaded again.+eval \{...\}/s; + + my $x = "$@"; + like( + $x, $re, + 'Overloaded stringification should include a stack trace' + ); + } +} + +# Test using message as hash key to constructor +{ + eval { Exception::Class::Base->throw( message => 'err' ); }; + + my $e = $@; + + is( + $e->error, 'err', + q{Exception's error message should be 'err'} + ); + + is( + $e->message, 'err', + q{Exception's message should be 'err'} + ); +} + +## no critic (Modules::ProhibitMultiplePackages) +{ + { + package X::Y; + + use Exception::Class (__PACKAGE__); + + sub xy_die () { __PACKAGE__->throw( error => 'dead' ); } + + eval {xy_die}; + } + + my $e = $@; + + is( + $e->error, 'dead', + q{Error message should be 'dead'} + ); +} + +# subclass overriding as_string + +sub Exc::AsString::as_string { return uc $_[0]->error } + +{ + eval { Exc::AsString->throw( error => 'upper case' ) }; + + my $e = $@; + + is( + "$e", 'UPPER CASE', + 'Overriding as_string in subclass' + ); +} + +# fields + +{ + eval { FieldsException->throw( error => 'error', foo => 5 ) }; + + my $e = $@; + + can_ok( $e, 'foo' ); + + is( + $e->foo, 5, + q{Exception's foo method should return 5} + ); + + is_deeply( + $@->field_hash, + { foo => 5, bar => undef }, + q{Exception's fields_hash should contain foo=>5,bar=>undef} + ); +} + +# more fields. +{ + eval { + MoreFieldsException->throw( error => 'error', yip => 10, foo => 15 ); + }; + + my $e = $@; + + can_ok( $e, 'foo' ); + + is( + $e->foo, 15, + q{Exception's foo method should return 15} + ); + + can_ok( $e, 'yip' ); + + is( + $e->yip, 10, + q{Exception's foo method should return 10} + ); +} + +sub FieldsException::full_message { + return join q{ }, $_[0]->message, 'foo = ' . $_[0]->foo; +} + +# fields + full_message + +{ + eval { FieldsException->throw( error => 'error', foo => 5 ) }; + + my $e = $@; + + like( + "$e", qr/error foo = 5/, + 'FieldsException should stringify to include the value of foo' + ); +} + +# single arg constructor +{ + eval { YAE->throw('foo') }; + + my $e = $@; + + ok( + $e, + 'Single arg constructor should work' + ); + + is( + $e->error, 'foo', + 'Single arg constructor should just set error/message' + ); +} + +# unsafe ref capture +{ + ObjectRefs2->UnsafeRefCapture(1); + + eval { Foo->new->bork2 }; + my $exc = $@; + + my @args = ( $exc->trace->frames )[1]->args; + + ok( + ref $args[0], + 'References should be saved in the stack trace' + ); +} + +# no refs - deprecated +{ + ObjectRefs3->NoRefs(0); + + eval { Foo->new->bork2 }; + my $exc = $@; + + my @args = ( $exc->trace->frames )[1]->args; + + ok( + ref $args[0], + 'References should be saved in the stack trace' + ); +} + +# aliases +{ + + package FooBar; + + use Exception::Class ( + 'SubAndFields' => { + fields => 'thing', + alias => 'throw_saf', + } + ); + + eval { throw_saf 'an error' }; + my $e = $@; + + ::ok( $e, 'Throw exception via convenience sub (one param)' ); + ::is( $e->error, 'an error', 'check error message' ); + + eval { throw_saf error => 'another error', thing => 10 }; + $e = $@; + + ::ok( $e, 'Throw exception via convenience sub (named params)' ); + ::is( $e->error, 'another error', 'check error message' ); + ::is( $e->thing, 10, 'check "thing" field' ); + + ::is( $e->package, __PACKAGE__, 'package matches current package' ); +} + +{ + + package BarBaz; + + use overload '""' => sub {'overloaded'}; +} + +{ + sub throw { TestException->throw( error => 'dead' ) } + + TestException->Trace(1); + + eval { throw( bless {}, 'BarBaz' ) }; + my $e = $@; + + unlike( + $e->as_string, qr/\boverloaded\b/, + 'overloading is ignored by default' + ); + + TestException->RespectOverload(1); + + eval { throw( bless {}, 'BarBaz' ) }; + $e = $@; + + like( $e->as_string, qr/\boverloaded\b/, 'overloading is now respected' ); +} + +{ + my %classes = map { $_ => 1 } Exception::Class::Classes(); + + ok( + $classes{TestException}, + 'TestException should be in the return from Classes()' + ); +} + +{ + sub throw2 { TestException->throw( error => 'dead' ); } + + eval { throw2('abcdefghijklmnop') }; + my $e = $@; + + like( + $e->as_string, qr/'abcdefghijklmnop'/, + 'arguments are not truncated by default' + ); + + TestException->MaxArgLength(10); + + eval { throw2('abcdefghijklmnop') }; + $e = $@; + + like( + $e->as_string, qr/'abcdefghij\.\.\.'/, + 'arguments are now truncated' + ); +} + +done_testing(); + +sub argh { + Exception::Class::Base->throw( error => 'ARGH' ); +} + +package Foo; + +sub new { + return bless {}, shift; +} + +sub bork { + my $self = shift; + + ObjectRefs->throw('kaboom'); +} + +sub bork2 { + my $self = shift; + + ObjectRefs2->throw('kaboom'); +} diff --git a/t/caught.t b/t/caught.t new file mode 100644 index 0000000..b725b86 --- /dev/null +++ b/t/caught.t @@ -0,0 +1,56 @@ +use strict; +use warnings; + +use Test::More; + +use Exception::Class ( + 'Foo', + 'Bar' => { isa => 'Foo' }, +); + +## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Exception::Class->caught('Bar'); + + ok( !$e, 'caught returns false for wrong class' ); +} + +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Bar->caught(); + + ok( !$e, 'caught returns false for wrong class' ); +} + +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Exception::Class->caught('Foo'); + + ok( $e, 'caught returns exception for correct class' ); + isa_ok( $e, 'Foo' ); + is( $e->message, 'foo', 'message is "foo"' ); +} + +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Foo->caught(); + + ok( $e, 'Foo->caught() returns exception' ); + isa_ok( $e, 'Foo' ); +} + +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Exception::Class->caught(); + + ok( $e, 'Foo->caught() returns exception' ); + isa_ok( $e, 'Foo' ); +} + +done_testing(); diff --git a/t/context.t b/t/context.t new file mode 100644 index 0000000..b6950eb --- /dev/null +++ b/t/context.t @@ -0,0 +1,30 @@ +use strict; +use warnings; + +use Test::More; + +use Exception::Class ( + 'Foo', + 'Bar' => { isa => 'Foo' }, +); + +Bar->NoContextInfo(1); + +## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) +{ + eval { Foo->throw( error => 'foo' ) }; + + my $e = Exception::Class->caught; + + ok( defined( $e->trace ), 'has trace detail' ); +} + +{ + eval { Bar->throw( error => 'foo' ) }; + + my $e = Exception::Class->caught; + + ok( !defined( $e->trace ), 'has no trace detail' ); +} + +done_testing(); diff --git a/t/ecb-standalone.t b/t/ecb-standalone.t new file mode 100644 index 0000000..adfcded --- /dev/null +++ b/t/ecb-standalone.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +use Test::More; + +{ + package MyE; + + use strict; + use warnings; + + use base 'Exception::Class::Base'; +} + +## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) +eval { MyE->throw() }; +isa_ok( $@, 'MyE', 'can throw MyE without loading Exception::Class' ); + +my $caught = MyE->caught(); +ok( $caught, 'caught MyE exception' ); + +done_testing(); diff --git a/t/ignore.t b/t/ignore.t new file mode 100644 index 0000000..7f40b4d --- /dev/null +++ b/t/ignore.t @@ -0,0 +1,98 @@ +use strict; +use warnings; + +use Test::More; + +## no critic (Modules::ProhibitMultiplePackages) +{ + package Foo; + + sub foo { + my $class = shift; + + TestException->throw(@_) unless $class eq 'Foo'; + + Bar->bar(@_); + } +} + +{ + package Bar; + + sub bar { + shift; + Baz->baz(@_); + } +} + +{ + package Baz; + + use base 'Foo'; + + sub baz { + shift->foo(@_); + } +} + +use strict; + +use Exception::Class qw(TestException); + +sub check_trace { + my ( $trace, $unwanted_pkg, $unwanted_class ) = @_; + + my ($bad_frame); + while ( my $frame = $trace->next_frame ) { + if ( ( grep { $frame->package eq $_ } @$unwanted_pkg ) + || ( grep { $frame->package->isa($_) } @$unwanted_class ) ) { + $bad_frame = $frame; + last; + } + } + + ok( !$bad_frame, 'Check for unwanted frames' ); + diag( 'Unwanted frame found: ' . $bad_frame->as_string ) + if $bad_frame; +} + +## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) +eval { Foo->foo() }; +my $e = $@; + +check_trace( $e->trace, [], [] ); + +eval { Foo->foo( ignore_package => ['Baz'] ) }; +$e = $@; + +check_trace( $e->trace, ['Baz'], [] ); + +eval { Foo->foo( ignore_class => ['Foo'] ) }; +$e = $@; + +check_trace( $e->trace, [], ['Foo'] ); + +eval { Foo->foo( ignore_package => [ 'Foo', 'Baz' ] ) }; +$e = $@; + +check_trace( $e->trace, [ 'Foo', 'Baz' ], [] ); + +eval { Foo->foo( skip_frames => 5 ) }; +$e = $@; + +check_trace( $e->trace, ['Baz'], [] ); + +eval { + Foo->foo( + frame_filter => sub { + my $p = shift; + return 0 if defined $p->{args}[0] && $p->{args}[0] eq 'Baz'; + return 1; + } + ); +}; +$e = $@; + +check_trace( $e->trace, ['Baz'], [] ); + +done_testing(); diff --git a/tidyall.ini b/tidyall.ini new file mode 100644 index 0000000..c143c6f --- /dev/null +++ b/tidyall.ini @@ -0,0 +1,23 @@ +[PerlCritic] +select = **/*.{pl,pm,t,psgi} +ignore = .build/**/* +ignore = Exception-Class-*/**/* +ignore = blib/**/* +ignore = t/00-* +ignore = t/author-* +ignore = t/release-* +ignore = t/zzz-* +ignore = xt/**/* +argv = --profile=$ROOT/perlcriticrc + +[PerlTidy] +select = **/*.{pl,pm,t,psgi} +ignore = .build/**/* +ignore = Exception-Class-*/**/* +ignore = blib/**/* +ignore = t/00-* +ignore = t/author-* +ignore = t/release-* +ignore = t/zzz-* +ignore = xt/**/* +argv = --profile=$ROOT/perltidyrc diff --git a/xt/author/00-compile.t b/xt/author/00-compile.t new file mode 100644 index 0000000..8435267 --- /dev/null +++ b/xt/author/00-compile.t @@ -0,0 +1,61 @@ +use 5.006; +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.057 + +use Test::More; + +plan tests => 3; + +my @module_files = ( + 'Exception/Class.pm', + 'Exception/Class/Base.pm' +); + + + +# no fake home requested + +my @switches = ( + -d 'blib' ? '-Mblib' : '-Ilib', +); + +use File::Spec; +use IPC::Open3; +use IO::Handle; + +open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; + +my @warnings; +for my $lib (@module_files) +{ + # see L + my $stderr = IO::Handle->new; + + diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } + $^X, @switches, '-e', "require q[$lib]")) + if $ENV{PERL_COMPILE_TEST_DEBUG}; + + my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); + binmode $stderr, ':crlf' if $^O eq 'MSWin32'; + my @_warnings = <$stderr>; + waitpid($pid, 0); + is($?, 0, "$lib loaded ok"); + + shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ + and not eval { +require blib; blib->VERSION('1.01') }; + + if (@_warnings) + { + warn @_warnings; + push @warnings, @_warnings; + } +} + + + +is(scalar(@warnings), 0, 'no warnings found') + or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ); + + diff --git a/xt/author/eol.t b/xt/author/eol.t new file mode 100644 index 0000000..ee3536b --- /dev/null +++ b/xt/author/eol.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19 + +use Test::More 0.88; +use Test::EOL; + +my @files = ( + 'lib/Exception/Class.pm', + 'lib/Exception/Class/Base.pm', + 't/00-report-prereqs.dd', + 't/00-report-prereqs.t', + 't/basic.t', + 't/caught.t', + 't/context.t', + 't/ecb-standalone.t', + 't/ignore.t' +); + +eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files; +done_testing; diff --git a/xt/author/mojibake.t b/xt/author/mojibake.t new file mode 100644 index 0000000..5ef161e --- /dev/null +++ b/xt/author/mojibake.t @@ -0,0 +1,9 @@ +#!perl + +use strict; +use warnings qw(all); + +use Test::More; +use Test::Mojibake; + +all_files_encoding_ok(); diff --git a/xt/author/no-tabs.t b/xt/author/no-tabs.t new file mode 100644 index 0000000..f366b6e --- /dev/null +++ b/xt/author/no-tabs.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15 + +use Test::More 0.88; +use Test::NoTabs; + +my @files = ( + 'lib/Exception/Class.pm', + 'lib/Exception/Class/Base.pm', + 't/00-report-prereqs.dd', + 't/00-report-prereqs.t', + 't/basic.t', + 't/caught.t', + 't/context.t', + 't/ecb-standalone.t', + 't/ignore.t' +); + +notabs_ok($_) foreach @files; +done_testing; diff --git a/xt/author/pod-coverage.t b/xt/author/pod-coverage.t new file mode 100644 index 0000000..f9ff28d --- /dev/null +++ b/xt/author/pod-coverage.t @@ -0,0 +1,44 @@ +#!perl +# This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable. + +use Test::Pod::Coverage 1.08; +use Test::More 0.88; + +BEGIN { + if ( $] <= 5.008008 ) { + plan skip_all => 'These tests require Pod::Coverage::TrustPod, which only works with Perl 5.8.9+'; + } +} +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 = (); + +my @also_private; + +for my $module ( sort @modules ) { + pod_coverage_ok( + $module, + { + coverage_class => 'Pod::Coverage::TrustPod', + also_private => \@also_private, + trustme => $trustme{$module} || [], + }, + "pod coverage for $module" + ); +} + +done_testing(); diff --git a/xt/author/pod-spell.t b/xt/author/pod-spell.t new file mode 100644 index 0000000..09ad34b --- /dev/null +++ b/xt/author/pod-spell.t @@ -0,0 +1,50 @@ +use strict; +use warnings; +use Test::More; + +# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007004 +use Test::Spelling 0.12; +use Pod::Wordlist; + + +add_stopwords(); +all_pod_files_spelling_ok( qw( bin lib ) ); +__DATA__ +0x62ash +Alexander +Arun +Base +Batyrshin +CPAN +Class +DROLSKY +DROLSKY's +Dave +Exception +Kumar's +Leon +PayPal +Ricardo +Rolsky +Rolsky's +Signes +Timmermans +Uncatchable +autarch +automagic +drolsky +egid +esque +fawaka +isa +lib +namespace +param +refcount +rethrow +rjbs +runtime +stacktrace +stringifies +subclasses +uncatchable diff --git a/xt/author/pod-syntax.t b/xt/author/pod-syntax.t new file mode 100644 index 0000000..e563e5d --- /dev/null +++ b/xt/author/pod-syntax.t @@ -0,0 +1,7 @@ +#!perl +# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. +use strict; use warnings; +use Test::More; +use Test::Pod 1.41; + +all_pod_files_ok(); diff --git a/xt/author/portability.t b/xt/author/portability.t new file mode 100644 index 0000000..c531252 --- /dev/null +++ b/xt/author/portability.t @@ -0,0 +1,10 @@ +use strict; +use warnings; + +use Test::More; + +eval 'use Test::Portability::Files'; +plan skip_all => 'Test::Portability::Files required for testing portability' + if $@; + +run_tests(); diff --git a/xt/author/test-version.t b/xt/author/test-version.t new file mode 100644 index 0000000..b47210e --- /dev/null +++ b/xt/author/test-version.t @@ -0,0 +1,23 @@ +use strict; +use warnings; +use Test::More; + +# generated by Dist::Zilla::Plugin::Test::Version 1.09 +use Test::Version; + +my @imports = qw( version_all_ok ); + +my $params = { + is_strict => 1, + has_version => 1, + multiple => 0, + +}; + +push @imports, $params + if version->parse( $Test::Version::VERSION ) >= version->parse('1.002'); + +Test::Version->import(@imports); + +version_all_ok; +done_testing; diff --git a/xt/author/tidyall.t b/xt/author/tidyall.t new file mode 100644 index 0000000..6236de5 --- /dev/null +++ b/xt/author/tidyall.t @@ -0,0 +1,16 @@ +# This file was automatically generated by Dist::Zilla::Plugin::Test::TidyAll v$VERSION + +use Test::More 0.88; +BEGIN { + if ( $] < 5.010 ) { + plan skip_all => 'This test requires Perl version 5.010'; + } +} +use Test::Code::TidyAll 0.24; + +tidyall_ok( + verbose => ( exists $ENV{TEST_TIDYALL_VERBOSE} ? $ENV{TEST_TIDYALL_VERBOSE} : 1 ), + jobs => ( exists $ENV{TEST_TIDYALL_JOBS} ? $ENV{TEST_TIDYALL_JOBS} : 4 ), +); + +done_testing; diff --git a/xt/release/cpan-changes.t b/xt/release/cpan-changes.t new file mode 100644 index 0000000..286005a --- /dev/null +++ b/xt/release/cpan-changes.t @@ -0,0 +1,10 @@ +use strict; +use warnings; + +# this test was generated with Dist::Zilla::Plugin::Test::CPAN::Changes 0.012 + +use Test::More 0.96 tests => 1; +use Test::CPAN::Changes; +subtest 'changes_ok' => sub { + changes_file_ok('Changes'); +}; diff --git a/xt/release/meta-json.t b/xt/release/meta-json.t new file mode 100644 index 0000000..5ddad73 --- /dev/null +++ b/xt/release/meta-json.t @@ -0,0 +1,4 @@ +#!perl + +use Test::CPAN::Meta::JSON; +meta_json_ok();