From: TizenOpenSource Date: Thu, 8 Feb 2024 09:32:25 +0000 (+0900) Subject: Imported Upstream version 1.36 X-Git-Tag: upstream/1.36^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=625d532ca4bd0098b96b6ed4af3e9c75aa088687;p=platform%2Fupstream%2Fperl-Class-Inspector.git Imported Upstream version 1.36 --- diff --git a/Changes b/Changes new file mode 100644 index 0000000..cef06bc --- /dev/null +++ b/Changes @@ -0,0 +1,166 @@ +Revision history for Perl extension Class-Inspector + +1.36 2019-07-19 08:35:53 -0400 + - Producton release identical to 1.35_01 + +1.35_01 2019-07-18 12:00:22 -0400 + - Fix bug in methods method that could cause it to modify @ISA (gh#11) + +1.34 2019-03-28 12:13:40 -0400 + - Producton release identical to 1.33_01 + +1.33_01 2019-03-27 23:30:09 -0400 + - Fix compatability with Devel::Hide (gh#6, gh#8). + +1.32 2017-08-08 14:12:42 -0400 + - The installed method now supports @INC hooks of any type + (coderef was supported as of 1.29, now arrayrefs and objects + are supported) + - Detect probably broken Perl on Cygwin in Makefile.PL (see gh#5) + +1.31 2016-11-25 09:33:47 -0500 + - Migrated from Module::Install to Dist::Zilla and ExtUtils::MakeMaker + - Fixed meta for repository which was pointing to the wrong URL + +1.30 23 Nov 2016 + - Moving to prod release + +1.29_02 23 Nov 2016 + - Update metadata to point to github repository. + Plus some other minor dist meta tweaks. + - Note: planning on doing a migration from Module::Install + to ExtUtils::MakeMaker shortly AFTER the next production + release. + +1.29_01 22 Nov 2016 + - Fix Makefile.PL to work with Perls without '.' in @INC + - Fix for the installed method when used with a PAR archive (rt#42846) + - Minor documentation fixes (grammar, spelling: rt#74481, rt#85356) + +1.28 Fri 19 Oct 2012 + - No functional changes + - Updating to Module::Install::DSL 1.06 + +1.27 Wed 25 Jan 2012 + - Moving to prod release + +1.26_01 Tue 24 Jan 2012 + - Updating to Module::Install::DSL 1.04 + - Updating copyright year + - Remove usage of defined @{"X::ISA"} to avoid warnings in 5.15.7 + and later Perl 5.16 (Tom Wyant) + +1.25 Thu 27 Jan 2011 + - Updating to Module::Install::DSL 1.00 + - Updating copyright year + +1.24 Tue 21 Apr 2009 + - Updating Perl dependency to 5.006 + - Updating to Module::Install::DSL 0.83 + - Add the Class::Inspector::Functions interface. + +1.23 Mon 2 Jun 2008 + - No functional changes + - Updating to Module::Install 0.75 + - Correcting the location of the author tests + +1.22 Sat 1 Mar 2008 + - 1.21_01 tested ok, moving to production version + - No changes + +1.21_01 Tue 12 Feb 2008 + - Adding experimental support for utf8 methods + (as per http://rt.cpan.org/Public/Bug/Display.html?id=28796) + +1.20 Tue 12 Feb 2008 + - CPAN Testers results look good for 1.19_01, + converting to a production release. + +1.19_01 Mon 11 Feb 2008 + - Bug fix to adapt to changes to File::Spec + +1.18 Thu 8 Nov 2007 + - Incremental release, no functional changes + - Updated to Module::Install 0.68 + (This brings META.yml to the current version) + - Updated versions of the automated tests that + were causing CPAN Testers failures. + +1.17 Mon 6 Aug 2007 + - Classes with leading numbers after the first :: are permitted + - Removing some old cruft from the tests + - Updated to Module::Install 0.65 + +1.16 Wed 10 May 2006 + - This release contains only build-time changes + - AutoInstall is only needed for options, so remove auto_install + +1.15 Sun 7 May 2006 + - This release contains only build-time changes + - Upgrading to Module::Install 0.62 + +1.14 Sun 8 Apr 2006 + - This release contains only build-time changes + - Moved from older CVS to newer SVN repository + - Upgraded to Module::Install 0.61 + +1.13 Wed Sep 28 2005 + - Fixed a minor POD bug in the synopsis + +1.12 Fri Sep 9 2005 + - Added a fix for classes with insanely broken ->isa methods + that cause Perl to die. + +1.11 Tue Sep 6 2005 + - It occured to me after I added ->find that what it _really_ is + is a way to find all the subclasses, but then include the class + itself in the returned list. This method makes much more sense + it I don't return the class itself, and rename it ->subclasses + - Fixed broken Makefile.PL + +1.10 Mon Sep 5 2005 + - Added the ->find method + - Cleaned up and reorganised the POD + - Made sure all return conditions are documented properly + - Converted to Module::Install + +1.09 skipped + +1.08 Tue Feb 15 2005 + - Removing braindead Build.PL + +1.07 Thu Nov 18 2004 + - Improved the speed (slightly, and only in positive cases) and accuracy of ->loaded. + - It now checks for some additional clues before returning false. + +1.06 Wed Jul 21 2004 + - Fixed a major Win32 bug + +1.05 Mon Jul 19 2004 + - Inlined a better version of self_and_super_class + - Removed Class::ISA as a dependency + +1.04 Tue Mar 23 2004 + - Apparently on Windows @INC/%INC uses Unix style backslashes + Updated to handle this fact. + +1.03 Sun Dec 14 2003 + - Fixed an infinite loop bug in recursive_children + +1.02 Sun Dec 14 2003 + - recursive children ignores ::ISA::CACHE:: + - Minor code tweaks + +1.01 Mon Nov 10 2003 + - Symbol table entries due to overloads and anonymous subs + are filtered from the results correctly. + - Did a large amount of code cleaning and optomising + +1.0 Sat Dec 21 13:31:21 2002 + - Converted to use File::Spec + +0.2 Tue May 28 18:47:00 2002 + - Added options for ->methods + +0.1 Thu May 23 20:09:55 2002 + - original version diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..47a0a7d --- /dev/null +++ b/INSTALL @@ -0,0 +1,72 @@ +This is the Perl distribution Class-Inspector. + +Installing Class-Inspector is straightforward. + +## Installation with cpanm + +If you have cpanm, you only need one line: + + % cpanm Class::Inspector + +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 Class::Inspector + +## Manual installation + +As a last resort, you can manually install it. Download the tarball, untar it, +install configure prerequisites (see below), then build it: + + % perl Makefile.PL + % make && make test + +Then install it: + + % make install + +On Windows platforms, you should use `dmake` or `nmake`, instead of `make`. + +If your perl is system-managed, you can create a local::lib in your home +directory to install modules to. For details, see the local::lib documentation: +https://metacpan.org/pod/local::lib + +The prerequisites of this distribution will also have to be installed manually. The +prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated +by running the manual build process described above. + +## Configure Prerequisites + +This distribution requires other modules to be installed before this +distribution's installer can be run. They can be found under the +"configure_requires" key of META.yml or the +"{prereqs}{configure}{requires}" key of META.json. + +## Other Prerequisites + +This distribution may require additional modules to be installed after running +Makefile.PL. +Look for prerequisites in the following phases: + +* to run make, PHASE = build +* to use the module code itself, PHASE = runtime +* to run tests, PHASE = test + +They can all be found in the "PHASE_requires" key of MYMETA.yml or the +"{prereqs}{PHASE}{requires}" key of MYMETA.json. + +## Documentation + +Class-Inspector documentation is available as POD. +You can run `perldoc` from a shell to read the documentation: + + % perldoc Class::Inspector + +For more information on installing Perl modules via CPAN, please see: +https://www.cpan.org/modules/INSTALL.html diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d903374 --- /dev/null +++ b/LICENSE @@ -0,0 +1,379 @@ +This software is copyright (c) 2002-2019 by Adam Kennedy. + +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) 2002-2019 by Adam Kennedy. + +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) 2002-2019 by Adam Kennedy. + +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..d5765bc --- /dev/null +++ b/MANIFEST @@ -0,0 +1,31 @@ +# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. +Changes +INSTALL +LICENSE +MANIFEST +META.json +META.yml +Makefile.PL +README +author.yml +dist.ini +lib/Class/Inspector.pm +lib/Class/Inspector/Functions.pm +maint/cip-before-install +maint/cip-test +perlcriticrc +t/00_diag.t +t/01_use.t +t/class_inspector.t +t/class_inspector__devel_hide.t +t/class_inspector__inc_to_local.t +t/class_inspector_functions.t +xt/author/critic.t +xt/author/eol.t +xt/author/no_tabs.t +xt/author/pod.t +xt/author/pod_coverage.t +xt/author/pod_spelling_common.t +xt/author/strict.t +xt/author/version.t +xt/release/fixme.t diff --git a/META.json b/META.json new file mode 100644 index 0000000..570990a --- /dev/null +++ b/META.json @@ -0,0 +1,87 @@ +{ + "abstract" : "Get information about a class and its structure", + "author" : [ + "Graham Ollis ", + "Adam Kennedy " + ], + "dynamic_config" : 0, + "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : 2 + }, + "name" : "Class-Inspector", + "prereqs" : { + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0", + "perl" : "5.008" + } + }, + "develop" : { + "requires" : { + "FindBin" : "0", + "Test2::Tools::PerlCritic" : "0", + "Test::EOL" : "0", + "Test::Fixme" : "0.07", + "Test::More" : "0.98", + "Test::NoTabs" : "0", + "Test::Pod" : "0", + "Test::Pod::Coverage" : "0", + "Test::Pod::Spelling::CommonMistakes" : "0", + "Test::Strict" : "0", + "YAML" : "0" + } + }, + "runtime" : { + "requires" : { + "File::Spec" : "0.80", + "base" : "0", + "perl" : "5.008" + } + }, + "test" : { + "requires" : { + "Test::More" : "0.98", + "perl" : "5.008" + } + } + }, + "provides" : { + "Class::Inspector" : { + "file" : "lib/Class/Inspector.pm", + "version" : "1.36" + }, + "Class::Inspector::Functions" : { + "file" : "lib/Class/Inspector/Functions.pm", + "version" : "1.36" + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "web" : "https://github.com/plicease/Class-Inspector/issues" + }, + "homepage" : "https://metacpan.org/pod/Class::Inspector", + "repository" : { + "type" : "git", + "url" : "git://github.com/plicease/Class-Inspector.git", + "web" : "https://github.com/plicease/Class-Inspector" + } + }, + "version" : "1.36", + "x_contributors" : [ + "Adam Kennedy ", + "Graham Ollis ", + "Tom Wyant", + "Steffen M\u00fcller", + "Kivanc Yazan (KYZN)" + ], + "x_generated_by_perl" : "v5.28.1", + "x_serialization_backend" : "Cpanel::JSON::XS version 4.12", + "x_use_unsafe_inc" : 0 +} + diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..e783417 --- /dev/null +++ b/META.yml @@ -0,0 +1,43 @@ +--- +abstract: 'Get information about a class and its structure' +author: + - 'Graham Ollis ' + - 'Adam Kennedy ' +build_requires: + Test::More: '0.98' + perl: '5.008' +configure_requires: + ExtUtils::MakeMaker: '0' + perl: '5.008' +dynamic_config: 0 +generated_by: 'Dist::Zilla version 6.012, 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: Class-Inspector +provides: + Class::Inspector: + file: lib/Class/Inspector.pm + version: '1.36' + Class::Inspector::Functions: + file: lib/Class/Inspector/Functions.pm + version: '1.36' +requires: + File::Spec: '0.80' + base: '0' + perl: '5.008' +resources: + bugtracker: https://github.com/plicease/Class-Inspector/issues + homepage: https://metacpan.org/pod/Class::Inspector + repository: git://github.com/plicease/Class-Inspector.git +version: '1.36' +x_contributors: + - 'Adam Kennedy ' + - 'Graham Ollis ' + - 'Tom Wyant' + - 'Steffen Müller' + - 'Kivanc Yazan (KYZN)' +x_generated_by_perl: v5.28.1 +x_serialization_backend: 'YAML::Tiny version 1.73' +x_use_unsafe_inc: 0 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..39c1147 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,67 @@ +BEGIN { + use strict; use warnings; + { + my $fn = 'Class/Inspector.pm'; + unless(index('lib/Class/Inspector.pm', $fn) == index('lib/Class/Inspector.pm', 'Class/Inspector.pm')) + { + print "I believe you have a broken Perl.\n"; + print "Please see https://github.com/plicease/Class-Inspector/issues/5\n"; + print "If you believe this diagnostic is mistaken, you can edit the Makefile.PL and comment out the logic that determins this.\n"; + print "If you believe this diagnostic is mistaken, feel free to comment on the issue above.\n"; + exit; + } + } + unless(eval q{ use 5.008; 1}) { + print "Perl 5.008 or better required\n"; + exit; + } +} +# This file was automatically generated by Dist::Zilla::Plugin::Author::Plicease::MakeMaker v2.37. +use strict; +use warnings; +use 5.008; +use ExtUtils::MakeMaker; + +my %WriteMakefileArgs = ( + "ABSTRACT" => "Get information about a class and its structure", + "AUTHOR" => "Graham Ollis , Adam Kennedy ", + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => 0 + }, + "DISTNAME" => "Class-Inspector", + "LICENSE" => "perl", + "MIN_PERL_VERSION" => "5.008", + "NAME" => "Class::Inspector", + "PM" => { + "lib/Class/Inspector.pm" => "\$(INST_LIB)/Class/Inspector.pm", + "lib/Class/Inspector/Functions.pm" => "\$(INST_LIB)/Class/Inspector/Functions.pm" + }, + "PREREQ_PM" => { + "File::Spec" => "0.80", + "base" => 0 + }, + "TEST_REQUIRES" => { + "Test::More" => "0.98" + }, + "VERSION" => "1.36", + "test" => { + "TESTS" => "t/*.t" + } +); + +my %FallbackPrereqs = ( + "File::Spec" => "0.80", + "Test::More" => "0.98", + "base" => 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); \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..c17315a --- /dev/null +++ b/README @@ -0,0 +1,255 @@ +NAME + + Class::Inspector - Get information about a class and its structure + +VERSION + + version 1.36 + +SYNOPSIS + + use Class::Inspector; + + # Is a class installed and/or loaded + Class::Inspector->installed( 'Foo::Class' ); + Class::Inspector->loaded( 'Foo::Class' ); + + # Filename related information + Class::Inspector->filename( 'Foo::Class' ); + Class::Inspector->resolved_filename( 'Foo::Class' ); + + # Get subroutine related information + Class::Inspector->functions( 'Foo::Class' ); + Class::Inspector->function_refs( 'Foo::Class' ); + Class::Inspector->function_exists( 'Foo::Class', 'bar' ); + Class::Inspector->methods( 'Foo::Class', 'full', 'public' ); + + # Find all loaded subclasses or something + Class::Inspector->subclasses( 'Foo::Class' ); + +DESCRIPTION + + Class::Inspector allows you to get information about a loaded class. + Most or all of this information can be found in other ways, but they + aren't always very friendly, and usually involve a relatively high + level of Perl wizardry, or strange and unusual looking code. + Class::Inspector attempts to provide an easier, more friendly interface + to this information. + +METHODS + + installed + + my $bool = Class::Inspector->installed($class); + + The installed static method tries to determine if a class is installed + on the machine, or at least available to Perl. It does this by wrapping + around resolved_filename. + + Returns true if installed/available, false if the class is not + installed, or undef if the class name is invalid. + + loaded + + my $bool = Class::Inspector->loaded($class); + + The loaded static method tries to determine if a class is loaded by + looking for symbol table entries. + + This method it uses to determine this will work even if the class does + not have its own file, but is contained inside a single file with + multiple classes in it. Even in the case of some sort of run-time + loading class being used, these typically leave some trace in the + symbol table, so an Autoload or Class::Autouse-based class should + correctly appear loaded. + + Returns true if the class is loaded, false if not, or undef if the + class name is invalid. + + filename + + my $filename = Class::Inspector->filename($class); + + For a given class, returns the base filename for the class. This will + NOT be a fully resolved filename, just the part of the filename BELOW + the @INC entry. + + print Class->filename( 'Foo::Bar' ); + > Foo/Bar.pm + + This filename will be returned with the right separator for the local + platform, and should work on all platforms. + + Returns the filename on success or undef if the class name is invalid. + + resolved_filename + + my $filename = Class::Inspector->resolved_filename($class); + my $filename = Class::Inspector->resolved_filename($class, @try_first); + + For a given class, the resolved_filename static method returns the + fully resolved filename for a class. That is, the file that the class + would be loaded from. + + This is not necessarily the file that the class WAS loaded from, as the + value returned is determined each time it runs, and the @INC include + path may change. + + To get the actual file for a loaded class, see the loaded_filename + method. + + Returns the filename for the class, or undef if the class name is + invalid. + + loaded_filename + + my $filename = Class::Inspector->loaded_filename($class); + + For a given loaded class, the loaded_filename static method determines + (via the %INC hash) the name of the file that it was originally loaded + from. + + Returns a resolved file path, or false if the class did not have it's + own file. + + functions + + my $arrayref = Class::Inspector->functions($class); + + For a loaded class, the functions static method returns a list of the + names of all the functions in the classes immediate namespace. + + Note that this is not the METHODS of the class, just the functions. + + Returns a reference to an array of the function names on success, or + undef if the class name is invalid or the class is not loaded. + + function_refs + + my $arrayref = Class::Inspector->function_refs($class); + + For a loaded class, the function_refs static method returns references + to all the functions in the classes immediate namespace. + + Note that this is not the METHODS of the class, just the functions. + + Returns a reference to an array of CODE refs of the functions on + success, or undef if the class is not loaded. + + function_exists + + my $bool = Class::Inspector->function_exists($class, $functon); + + Given a class and function name the function_exists static method will + check to see if the function exists in the class. + + Note that this is as a function, not as a method. To see if a method + exists for a class, use the can method for any class or object. + + Returns true if the function exists, false if not, or undef if the + class or function name are invalid, or the class is not loaded. + + methods + + my $arrayref = Class::Inspector->methods($class, @options); + + For a given class name, the methods static method will returns ALL the + methods available to that class. This includes all methods available + from every class up the class' @ISA tree. + + Returns a reference to an array of the names of all the available + methods on success, or undef if the class name is invalid or the class + is not loaded. + + A number of options are available to the methods method that will alter + the results returned. These should be listed after the class name, in + any order. + + # Only get public methods + my $method = Class::Inspector->methods( 'My::Class', 'public' ); + + public + + The public option will return only 'public' methods, as defined by + the Perl convention of prepending an underscore to any 'private' + methods. The public option will effectively remove any methods that + start with an underscore. + + private + + The private options will return only 'private' methods, as defined by + the Perl convention of prepending an underscore to an private + methods. The private option will effectively remove an method that do + not start with an underscore. + + Note: The public and private options are mutually exclusive + + full + + methods normally returns just the method name. Supplying the full + option will cause the methods to be returned as the full names. That + is, instead of returning [ 'method1', 'method2', 'method3' ], you + would instead get [ 'Class::method1', 'AnotherClass::method2', + 'Class::method3' ]. + + expanded + + The expanded option will cause a lot more information about method to + be returned. Instead of just the method name, you will instead get an + array reference containing the method name as a single combined name, + a la full, the separate class and method, and a CODE ref to the + actual function ( if available ). Please note that the function + reference is not guaranteed to be available. Class::Inspector is + intended at some later time, to work with modules that have some kind + of common run-time loader in place ( e.g Autoloader or Class::Autouse + for example. + + The response from methods( 'Class', 'expanded' ) would look something + like the following. + + [ + [ 'Class::method1', 'Class', 'method1', \&Class::method1 ], + [ 'Another::method2', 'Another', 'method2', \&Another::method2 ], + [ 'Foo::bar', 'Foo', 'bar', \&Foo::bar ], + ] + + subclasses + + my $arrayref = Class::Inspector->subclasses($class); + + The subclasses static method will search then entire namespace (and + thus all currently loaded classes) to find all classes that are + subclasses of the class provided as a the parameter. + + The actual test will be done by calling isa on the class as a static + method. (i.e. My::Class->isa($class). + + Returns a reference to a list of the loaded classes that match the + class provided, or false is none match, or undef if the class name + provided is invalid. + +SEE ALSO + + http://ali.as/, Class::Handle, Class::Inspector::Functions + +AUTHOR + + Original author: Adam Kennedy + + Current maintainer: Graham Ollis + + Contributors: + + Tom Wyant + + Steffen Müller + + Kivanc Yazan (KYZN) + +COPYRIGHT AND LICENSE + + This software is copyright (c) 2002-2019 by Adam Kennedy. + + This is free software; you can redistribute it and/or modify it under + the same terms as the Perl 5 programming language system itself. + diff --git a/author.yml b/author.yml new file mode 100644 index 0000000..0e13d19 --- /dev/null +++ b/author.yml @@ -0,0 +1,15 @@ +--- +pod_spelling_system: + skip: 0 + stopwords: + - Müller + - Steffen + - Wyant + + +pod_coverage: + skip: 0 + private: + - Class::Inspector#children + - Class::Inspector#recursive_children + - Class::Inspector::Functions diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..1581a9c --- /dev/null +++ b/dist.ini @@ -0,0 +1,61 @@ +name = Class-Inspector +author = Graham Ollis +author = Adam Kennedy +license = Perl_5 +copyright_holder = Adam Kennedy +copyright_year = 2002-2019 +version = 1.36 + +[@Author::Plicease] +:version = 2.37 +travis_status = 1 +release_tests = 1 + +preamble = | { +preamble = | my $fn = 'Class/Inspector.pm'; +preamble = | unless(index('lib/Class/Inspector.pm', $fn) == index('lib/Class/Inspector.pm', 'Class/Inspector.pm')) +preamble = | { +preamble = | print "I believe you have a broken Perl.\n"; +preamble = | print "Please see https://github.com/plicease/Class-Inspector/issues/5\n"; +preamble = | print "If you believe this diagnostic is mistaken, you can edit the Makefile.PL and comment out the logic that determins this.\n"; +preamble = | print "If you believe this diagnostic is mistaken, feel free to comment on the issue above.\n"; +preamble = | exit; +preamble = | } +preamble = | } + +[Prereqs / TestPrereqs] +-phase = test +; TODO: this gets overridden with 0.94 +Test::More = 0.47 + +[Prereqs] +File::Spec = 0.80 + +[RemovePrereqs] +; comes with Perl 5.6.0 or better +remove = strict +remove = warnings +remove = vars +remove = constant +remove = utf8 +remove = Exporter + +; optional test dep +remove = Devel::Hide + +[Author::Plicease::Upload] +cpan = 1 + +[Author::Plicease::Thanks] +current = Graham Ollis +original = Adam Kennedy +contributor = Tom Wyant +contributor = Steffen Müller +contributor = Kivanc Yazan (KYZN) + +[MetaProvides::Package] + +[PruneFiles] +filename = xt/release/changes.t +; some unicode issue needs to be resolved: +filename = xt/author/pod_spelling_system.t diff --git a/lib/Class/Inspector.pm b/lib/Class/Inspector.pm new file mode 100644 index 0000000..4911d12 --- /dev/null +++ b/lib/Class/Inspector.pm @@ -0,0 +1,662 @@ +package Class::Inspector; + +use 5.006; +# We don't want to use strict refs anywhere in this module, since we do a +# lot of things in here that aren't strict refs friendly. +use strict qw{vars subs}; +use warnings; +use File::Spec (); + +# ABSTRACT: Get information about a class and its structure +our $VERSION = '1.36'; # VERSION + + +# If Unicode is available, enable it so that the +# pattern matches below match unicode method names. +# We can safely ignore any failure here. +BEGIN { + local $@; + eval { + require utf8; + utf8->import; + }; +} + +# Predefine some regexs +our $RE_IDENTIFIER = qr/\A[^\W\d]\w*\z/s; +our $RE_CLASS = qr/\A[^\W\d]\w*(?:(?:\'|::)\w+)*\z/s; + +# Are we on something Unix-like? +our $UNIX = !! ( $File::Spec::ISA[0] eq 'File::Spec::Unix' ); + + +##################################################################### +# Basic Methods + + +sub _resolved_inc_handler { + my $class = shift; + my $filename = $class->_inc_filename(shift) or return undef; + + foreach my $inc ( @INC ) { + my $ref = ref $inc; + if($ref eq 'CODE') { + my @ret = $inc->($inc, $filename); + if(@ret == 1 && ! defined $ret[0]) { + # do nothing. + } elsif(@ret) { + return 1; + } + } + elsif($ref eq 'ARRAY' && ref($inc->[0]) eq 'CODE') { + my @ret = $inc->[0]->($inc, $filename); + if(@ret) { + return 1; + } + } + elsif($ref && eval { $inc->can('INC') }) { + my @ret = $inc->INC($filename); + if(@ret) { + return 1; + } + } + } + + ''; +} + +sub installed { + my $class = shift; + !! ($class->loaded_filename($_[0]) or $class->resolved_filename($_[0]) or $class->_resolved_inc_handler($_[0])); +} + + +sub loaded { + my $class = shift; + my $name = $class->_class(shift) or return undef; + $class->_loaded($name); +} + +sub _loaded { + my $class = shift; + my $name = shift; + + # Handle by far the two most common cases + # This is very fast and handles 99% of cases. + return 1 if defined ${"${name}::VERSION"}; + return 1 if @{"${name}::ISA"}; + + # Are there any symbol table entries other than other namespaces + foreach ( keys %{"${name}::"} ) { + next if substr($_, -2, 2) eq '::'; + return 1 if defined &{"${name}::$_"}; + } + + # No functions, and it doesn't have a version, and isn't anything. + # As an absolute last resort, check for an entry in %INC + my $filename = $class->_inc_filename($name); + return 1 if defined $INC{$filename}; + + ''; +} + + +sub filename { + my $class = shift; + my $name = $class->_class(shift) or return undef; + File::Spec->catfile( split /(?:\'|::)/, $name ) . '.pm'; +} + + +sub resolved_filename { + my $class = shift; + my $filename = $class->_inc_filename(shift) or return undef; + my @try_first = @_; + + # Look through the @INC path to find the file + foreach ( @try_first, @INC ) { + my $full = "$_/$filename"; + next unless -e $full; + return $UNIX ? $full : $class->_inc_to_local($full); + } + + # File not found + ''; +} + + +sub loaded_filename { + my $class = shift; + my $filename = $class->_inc_filename(shift); + $UNIX ? $INC{$filename} : $class->_inc_to_local($INC{$filename}); +} + + + + + +##################################################################### +# Sub Related Methods + + +sub functions { + my $class = shift; + my $name = $class->_class(shift) or return undef; + return undef unless $class->loaded( $name ); + + # Get all the CODE symbol table entries + my @functions = sort grep { /$RE_IDENTIFIER/o } + grep { defined &{"${name}::$_"} } + keys %{"${name}::"}; + \@functions; +} + + +sub function_refs { + my $class = shift; + my $name = $class->_class(shift) or return undef; + return undef unless $class->loaded( $name ); + + # Get all the CODE symbol table entries, but return + # the actual CODE refs this time. + my @functions = map { \&{"${name}::$_"} } + sort grep { /$RE_IDENTIFIER/o } + grep { defined &{"${name}::$_"} } + keys %{"${name}::"}; + \@functions; +} + + +sub function_exists { + my $class = shift; + my $name = $class->_class( shift ) or return undef; + my $function = shift or return undef; + + # Only works if the class is loaded + return undef unless $class->loaded( $name ); + + # Does the GLOB exist and its CODE part exist + defined &{"${name}::$function"}; +} + + +sub methods { + my $class = shift; + my $name = $class->_class( shift ) or return undef; + my @arguments = map { lc $_ } @_; + + # Process the arguments to determine the options + my %options = (); + foreach ( @arguments ) { + if ( $_ eq 'public' ) { + # Only get public methods + return undef if $options{private}; + $options{public} = 1; + + } elsif ( $_ eq 'private' ) { + # Only get private methods + return undef if $options{public}; + $options{private} = 1; + + } elsif ( $_ eq 'full' ) { + # Return the full method name + return undef if $options{expanded}; + $options{full} = 1; + + } elsif ( $_ eq 'expanded' ) { + # Returns class, method and function ref + return undef if $options{full}; + $options{expanded} = 1; + + } else { + # Unknown or unsupported options + return undef; + } + } + + # Only works if the class is loaded + return undef unless $class->loaded( $name ); + + # Get the super path ( not including UNIVERSAL ) + # Rather than using Class::ISA, we'll use an inlined version + # that implements the same basic algorithm. + my @path = (); + my @queue = ( $name ); + my %seen = ( $name => 1 ); + while ( my $cl = shift @queue ) { + push @path, $cl; + unshift @queue, grep { ! $seen{$_}++ } + map { s/^::/main::/; s/\'/::/g; $_ } ## no critic + map { "$_" } + ( @{"${cl}::ISA"} ); + } + + # Find and merge the function names across the entire super path. + # Sort alphabetically and return. + my %methods = (); + foreach my $namespace ( @path ) { + my @functions = grep { ! $methods{$_} } + grep { /$RE_IDENTIFIER/o } + grep { defined &{"${namespace}::$_"} } + keys %{"${namespace}::"}; + foreach ( @functions ) { + $methods{$_} = $namespace; + } + } + + # Filter to public or private methods if needed + my @methodlist = sort keys %methods; + @methodlist = grep { ! /^\_/ } @methodlist if $options{public}; + @methodlist = grep { /^\_/ } @methodlist if $options{private}; + + # Return in the correct format + @methodlist = map { "$methods{$_}::$_" } @methodlist if $options{full}; + @methodlist = map { + [ "$methods{$_}::$_", $methods{$_}, $_, \&{"$methods{$_}::$_"} ] + } @methodlist if $options{expanded}; + + \@methodlist; +} + + + + + +##################################################################### +# Search Methods + + +sub subclasses { + my $class = shift; + my $name = $class->_class( shift ) or return undef; + + # Prepare the search queue + my @found = (); + my @queue = grep { $_ ne 'main' } $class->_subnames(''); + while ( @queue ) { + my $c = shift(@queue); # c for class + if ( $class->_loaded($c) ) { + # At least one person has managed to misengineer + # a situation in which ->isa could die, even if the + # class is real. Trap these cases and just skip + # over that (bizarre) class. That would at limit + # problems with finding subclasses to only the + # modules that have broken ->isa implementation. + local $@; + eval { + if ( $c->isa($name) ) { + # Add to the found list, but don't add the class itself + push @found, $c unless $c eq $name; + } + }; + } + + # Add any child namespaces to the head of the queue. + # This keeps the queue length shorted, and allows us + # not to have to do another sort at the end. + unshift @queue, map { "${c}::$_" } $class->_subnames($c); + } + + @found ? \@found : ''; +} + +sub _subnames { + my ($class, $name) = @_; + return sort + grep { ## no critic + substr($_, -2, 2, '') eq '::' + and + /$RE_IDENTIFIER/o + } + keys %{"${name}::"}; +} + + + + + +##################################################################### +# Children Related Methods + +# These can go undocumented for now, until I decide if its best to +# just search the children in namespace only, or if I should do it via +# the file system. + +# Find all the loaded classes below us +sub children { + my $class = shift; + my $name = $class->_class(shift) or return (); + + # Find all the Foo:: elements in our symbol table + no strict 'refs'; + map { "${name}::$_" } sort grep { s/::$// } keys %{"${name}::"}; ## no critic +} + +# As above, but recursively +sub recursive_children { + my $class = shift; + my $name = $class->_class(shift) or return (); + my @children = ( $name ); + + # Do the search using a nicer, more memory efficient + # variant of actual recursion. + my $i = 0; + no strict 'refs'; + while ( my $namespace = $children[$i++] ) { + push @children, map { "${namespace}::$_" } + grep { ! /^::/ } # Ignore things like ::ISA::CACHE:: + grep { s/::$// } ## no critic + keys %{"${namespace}::"}; + } + + sort @children; +} + + + + + +##################################################################### +# Private Methods + +# Checks and expands ( if needed ) a class name +sub _class { + my $class = shift; + my $name = shift or return ''; + + # Handle main shorthand + return 'main' if $name eq '::'; + $name =~ s/\A::/main::/; + + # Check the class name is valid + $name =~ /$RE_CLASS/o ? $name : ''; +} + +# Create a INC-specific filename, which always uses '/' +# regardless of platform. +sub _inc_filename { + my $class = shift; + my $name = $class->_class(shift) or return undef; + join( '/', split /(?:\'|::)/, $name ) . '.pm'; +} + +# Convert INC-specific file name to local file name +sub _inc_to_local { + # Shortcut in the Unix case + return $_[1] if $UNIX; + + # On other places, we have to deal with an unusual path that might look + # like C:/foo/bar.pm which doesn't fit ANY normal pattern. + # Putting it through splitpath/dir and back again seems to normalise + # it to a reasonable amount. + my $class = shift; + my $inc_name = shift or return undef; + my ($vol, $dir, $file) = File::Spec->splitpath( $inc_name ); + $dir = File::Spec->catdir( File::Spec->splitdir( $dir || "" ) ); + File::Spec->catpath( $vol, $dir, $file || "" ); +} + +1; + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Class::Inspector - Get information about a class and its structure + +=head1 VERSION + +version 1.36 + +=head1 SYNOPSIS + + use Class::Inspector; + + # Is a class installed and/or loaded + Class::Inspector->installed( 'Foo::Class' ); + Class::Inspector->loaded( 'Foo::Class' ); + + # Filename related information + Class::Inspector->filename( 'Foo::Class' ); + Class::Inspector->resolved_filename( 'Foo::Class' ); + + # Get subroutine related information + Class::Inspector->functions( 'Foo::Class' ); + Class::Inspector->function_refs( 'Foo::Class' ); + Class::Inspector->function_exists( 'Foo::Class', 'bar' ); + Class::Inspector->methods( 'Foo::Class', 'full', 'public' ); + + # Find all loaded subclasses or something + Class::Inspector->subclasses( 'Foo::Class' ); + +=head1 DESCRIPTION + +Class::Inspector allows you to get information about a loaded class. Most or +all of this information can be found in other ways, but they aren't always +very friendly, and usually involve a relatively high level of Perl wizardry, +or strange and unusual looking code. Class::Inspector attempts to provide +an easier, more friendly interface to this information. + +=head1 METHODS + +=head2 installed + + my $bool = Class::Inspector->installed($class); + +The C static method tries to determine if a class is installed +on the machine, or at least available to Perl. It does this by wrapping +around C. + +Returns true if installed/available, false if the class is not installed, +or C if the class name is invalid. + +=head2 loaded + + my $bool = Class::Inspector->loaded($class); + +The C static method tries to determine if a class is loaded by +looking for symbol table entries. + +This method it uses to determine this will work even if the class does not +have its own file, but is contained inside a single file with multiple +classes in it. Even in the case of some sort of run-time loading class +being used, these typically leave some trace in the symbol table, so an +L or L-based class should correctly appear +loaded. + +Returns true if the class is loaded, false if not, or C if the +class name is invalid. + +=head2 filename + + my $filename = Class::Inspector->filename($class); + +For a given class, returns the base filename for the class. This will NOT +be a fully resolved filename, just the part of the filename BELOW the +C<@INC> entry. + + print Class->filename( 'Foo::Bar' ); + > Foo/Bar.pm + +This filename will be returned with the right separator for the local +platform, and should work on all platforms. + +Returns the filename on success or C if the class name is invalid. + +=head2 resolved_filename + + my $filename = Class::Inspector->resolved_filename($class); + my $filename = Class::Inspector->resolved_filename($class, @try_first); + +For a given class, the C static method returns the fully +resolved filename for a class. That is, the file that the class would be +loaded from. + +This is not necessarily the file that the class WAS loaded from, as the +value returned is determined each time it runs, and the C<@INC> include +path may change. + +To get the actual file for a loaded class, see the C +method. + +Returns the filename for the class, or C if the class name is +invalid. + +=head2 loaded_filename + + my $filename = Class::Inspector->loaded_filename($class); + +For a given loaded class, the C static method determines +(via the C<%INC> hash) the name of the file that it was originally loaded +from. + +Returns a resolved file path, or false if the class did not have it's own +file. + +=head2 functions + + my $arrayref = Class::Inspector->functions($class); + +For a loaded class, the C static method returns a list of the +names of all the functions in the classes immediate namespace. + +Note that this is not the METHODS of the class, just the functions. + +Returns a reference to an array of the function names on success, or C +if the class name is invalid or the class is not loaded. + +=head2 function_refs + + my $arrayref = Class::Inspector->function_refs($class); + +For a loaded class, the C static method returns references to +all the functions in the classes immediate namespace. + +Note that this is not the METHODS of the class, just the functions. + +Returns a reference to an array of C refs of the functions on +success, or C if the class is not loaded. + +=head2 function_exists + + my $bool = Class::Inspector->function_exists($class, $functon); + +Given a class and function name the C static method will +check to see if the function exists in the class. + +Note that this is as a function, not as a method. To see if a method +exists for a class, use the C method for any class or object. + +Returns true if the function exists, false if not, or C if the +class or function name are invalid, or the class is not loaded. + +=head2 methods + + my $arrayref = Class::Inspector->methods($class, @options); + +For a given class name, the C static method will returns ALL +the methods available to that class. This includes all methods available +from every class up the class' C<@ISA> tree. + +Returns a reference to an array of the names of all the available methods +on success, or C if the class name is invalid or the class is not +loaded. + +A number of options are available to the C method that will alter +the results returned. These should be listed after the class name, in any +order. + + # Only get public methods + my $method = Class::Inspector->methods( 'My::Class', 'public' ); + +=over 4 + +=item public + +The C option will return only 'public' methods, as defined by the Perl +convention of prepending an underscore to any 'private' methods. The C +option will effectively remove any methods that start with an underscore. + +=item private + +The C options will return only 'private' methods, as defined by the +Perl convention of prepending an underscore to an private methods. The +C option will effectively remove an method that do not start with an +underscore. + +B and C options are mutually exclusive> + +=item full + +C normally returns just the method name. Supplying the C option +will cause the methods to be returned as the full names. That is, instead of +returning C<[ 'method1', 'method2', 'method3' ]>, you would instead get +C<[ 'Class::method1', 'AnotherClass::method2', 'Class::method3' ]>. + +=item expanded + +The C option will cause a lot more information about method to be +returned. Instead of just the method name, you will instead get an array +reference containing the method name as a single combined name, a la C, +the separate class and method, and a CODE ref to the actual function ( if +available ). Please note that the function reference is not guaranteed to +be available. C is intended at some later time, to work +with modules that have some kind of common run-time loader in place ( e.g +C or C for example. + +The response from C would look something like +the following. + + [ + [ 'Class::method1', 'Class', 'method1', \&Class::method1 ], + [ 'Another::method2', 'Another', 'method2', \&Another::method2 ], + [ 'Foo::bar', 'Foo', 'bar', \&Foo::bar ], + ] + +=back + +=head2 subclasses + + my $arrayref = Class::Inspector->subclasses($class); + +The C static method will search then entire namespace (and thus +B currently loaded classes) to find all classes that are subclasses +of the class provided as a the parameter. + +The actual test will be done by calling C on the class as a static +method. (i.e. Cisa($class)>. + +Returns a reference to a list of the loaded classes that match the class +provided, or false is none match, or C if the class name provided +is invalid. + +=head1 SEE ALSO + +L, L, L + +=head1 AUTHOR + +Original author: Adam Kennedy Eadamk@cpan.orgE + +Current maintainer: Graham Ollis Eplicease@cpan.orgE + +Contributors: + +Tom Wyant + +Steffen Müller + +Kivanc Yazan (KYZN) + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2002-2019 by Adam Kennedy. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/lib/Class/Inspector/Functions.pm b/lib/Class/Inspector/Functions.pm new file mode 100644 index 0000000..b05b9bb --- /dev/null +++ b/lib/Class/Inspector/Functions.pm @@ -0,0 +1,136 @@ +package Class::Inspector::Functions; + +use 5.006; +use strict; +use warnings; +use Exporter (); +use Class::Inspector (); +use base qw( Exporter ); + +# ABSTRACT: Get information about a class and its structure +our $VERSION = '1.36'; # VERSION + +BEGIN { + + our @EXPORT = qw( + installed + loaded + + filename + functions + methods + + subclasses + ); + + our @EXPORT_OK = qw( + resolved_filename + loaded_filename + + function_refs + function_exists + ); + #children + #recursive_children + + our %EXPORT_TAGS = ( ALL => [ @EXPORT_OK, @EXPORT ] ); + + foreach my $meth (@EXPORT, @EXPORT_OK) { + my $sub = Class::Inspector->can($meth); + no strict 'refs'; + *{$meth} = sub {&$sub('Class::Inspector', @_)}; + } + +} + +1; + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Class::Inspector::Functions - Get information about a class and its structure + +=head1 VERSION + +version 1.36 + +=head1 SYNOPSIS + + use Class::Inspector::Functions; + # Class::Inspector provides a non-polluting, + # method based interface! + + # Is a class installed and/or loaded + installed( 'Foo::Class' ); + loaded( 'Foo::Class' ); + + # Filename related information + filename( 'Foo::Class' ); + resolved_filename( 'Foo::Class' ); + + # Get subroutine related information + functions( 'Foo::Class' ); + function_refs( 'Foo::Class' ); + function_exists( 'Foo::Class', 'bar' ); + methods( 'Foo::Class', 'full', 'public' ); + + # Find all loaded subclasses or something + subclasses( 'Foo::Class' ); + +=head1 DESCRIPTION + +Class::Inspector::Functions is a function based interface of +L. For a thorough documentation of the available +functions, please check the manual for the main module. + +=head2 Exports + +The following functions are exported by default. + + installed + loaded + filename + functions + methods + subclasses + +The following functions are exported only by request. + + resolved_filename + loaded_filename + function_refs + function_exists + +All the functions may be imported using the C<:ALL> tag. + +=head1 SEE ALSO + +L, L, L + +=head1 AUTHOR + +Original author: Adam Kennedy Eadamk@cpan.orgE + +Current maintainer: Graham Ollis Eplicease@cpan.orgE + +Contributors: + +Tom Wyant + +Steffen Müller + +Kivanc Yazan (KYZN) + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2002-2019 by Adam Kennedy. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/maint/cip-before-install b/maint/cip-before-install new file mode 100755 index 0000000..c6a804d --- /dev/null +++ b/maint/cip-before-install @@ -0,0 +1,12 @@ +#!/bin/bash + +set -ex + +cip exec cpanm Devel::Hide +cip sudo dzil-cpanm Dist::Zilla::Plugin::OurPkgVersion + +if [ ! -z "$PERL_CRITIC" ]; then + cip exec cpanm -n Perl::Critic Perl::Critic::Freenode https://dist.wdlabs.com/Test2-Tools-PerlCritic-0.01.tar.gz + cip exec cpanm -n Test::EOL Test::Pod Test::Pod::Coverage Test::Pod::Spelling::CommonMistakes Test::Strict Test::Version Test::Fixme \ + Test::NoTabs YAML +fi diff --git a/maint/cip-test b/maint/cip-test new file mode 100755 index 0000000..82e851d --- /dev/null +++ b/maint/cip-test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex + +perl Makefile.PL +make +make test TEST_VERBOSE=1 +prove -lr xt diff --git a/perlcriticrc b/perlcriticrc new file mode 100644 index 0000000..5241092 --- /dev/null +++ b/perlcriticrc @@ -0,0 +1,60 @@ +severity = 1 +only = 1 + +[Freenode::ArrayAssignAref] +[Freenode::BarewordFilehandles] +[Freenode::ConditionalDeclarations] +[Freenode::ConditionalImplicitReturn] +[Freenode::DeprecatedFeatures] +[Freenode::DiscouragedModules] +[Freenode::DollarAB] +[Freenode::Each] +[Freenode::EmptyReturn] +[Freenode::IndirectObjectNotation] +[Freenode::LexicalForeachIterator] +[Freenode::LoopOnHash] +[Freenode::ModPerl] +[Freenode::OpenArgs] +[Freenode::OverloadOptions] +[Freenode::POSIXImports] +[Freenode::PackageMatchesFilename] +[Freenode::PreferredAlternatives] +[Freenode::Prototypes] +[Freenode::StrictWarnings] +extra_importers = Test2::V0 +[Freenode::Threads] +[Freenode::Wantarray] +[Freenode::WarningsSwitch] +[Freenode::WhileDiamondDefaultAssignment] + +[BuiltinFunctions::ProhibitBooleanGrep] +[BuiltinFunctions::ProhibitStringyEval] +[BuiltinFunctions::ProhibitStringySplit] +[BuiltinFunctions::ProhibitVoidGrep] +[BuiltinFunctions::ProhibitVoidMap] +[ClassHierarchies::ProhibitExplicitISA] +[ClassHierarchies::ProhibitOneArgBless] +[CodeLayout::ProhibitHardTabs] +allow_leading_tabs = 0 +[CodeLayout::ProhibitTrailingWhitespace] +[CodeLayout::RequireConsistentNewlines] +[ControlStructures::ProhibitLabelsWithSpecialBlockNames] +[ControlStructures::ProhibitMutatingListFunctions] +[ControlStructures::ProhibitUnreachableCode] +[InputOutput::ProhibitBarewordFileHandles] +[InputOutput::ProhibitJoinedReadline] +[InputOutput::ProhibitTwoArgOpen] +[Miscellanea::ProhibitFormats] +[Miscellanea::ProhibitUselessNoCritic] +[Modules::ProhibitConditionalUseStatements] +;[Modules::RequireEndWithOne] +[Modules::RequireNoMatchVarsWithUseEnglish] +[Objects::ProhibitIndirectSyntax] +[RegularExpressions::ProhibitUselessTopic] +[Subroutines::ProhibitNestedSubs] +[ValuesAndExpressions::ProhibitLeadingZeros] +[ValuesAndExpressions::ProhibitMixedBooleanOperators] +[ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator] +[ValuesAndExpressions::RequireUpperCaseHeredocTerminator] +[Variables::ProhibitPerl4PackageNames] +[Variables::ProhibitUnusedVariables] diff --git a/t/00_diag.t b/t/00_diag.t new file mode 100644 index 0000000..f1e814f --- /dev/null +++ b/t/00_diag.t @@ -0,0 +1,85 @@ +use strict; +use warnings; +use Config; +use Test::More tests => 1; + +# This .t file is generated. +# make changes instead to dist.ini + +my %modules; +my $post_diag; + +$modules{$_} = $_ for qw( + ExtUtils::MakeMaker + File::Spec + Test::More +); + + + +my @modules = sort keys %modules; + +sub spacer () +{ + diag ''; + diag ''; + diag ''; +} + +pass 'okay'; + +my $max = 1; +$max = $_ > $max ? $_ : $max for map { length $_ } @modules; +our $format = "%-${max}s %s"; + +spacer; + +my @keys = sort grep /(MOJO|PERL|\A(LC|HARNESS)_|\A(SHELL|LANG)\Z)/i, keys %ENV; + +if(@keys > 0) +{ + diag "$_=$ENV{$_}" for @keys; + + if($ENV{PERL5LIB}) + { + spacer; + diag "PERL5LIB path"; + diag $_ for split $Config{path_sep}, $ENV{PERL5LIB}; + + } + elsif($ENV{PERLLIB}) + { + spacer; + diag "PERLLIB path"; + diag $_ for split $Config{path_sep}, $ENV{PERLLIB}; + } + + spacer; +} + +diag sprintf $format, 'perl ', $]; + +foreach my $module (@modules) +{ + my $pm = "$module.pm"; + $pm =~ s{::}{/}g; + if(eval { require $pm; 1 }) + { + my $ver = eval { $module->VERSION }; + $ver = 'undef' unless defined $ver; + diag sprintf $format, $module, $ver; + } + else + { + diag sprintf $format, $module, '-'; + } +} + +if($post_diag) +{ + spacer; + $post_diag->(); +} + +spacer; + diff --git a/t/01_use.t b/t/01_use.t new file mode 100644 index 0000000..f501820 --- /dev/null +++ b/t/01_use.t @@ -0,0 +1,8 @@ +use strict; +use warnings; +use Test::More tests => 3; + +ok( $] >= 5.006, "Your perl is new enough" ); + +use_ok('Class::Inspector'); +use_ok('Class::Inspector::Functions'); diff --git a/t/class_inspector.t b/t/class_inspector.t new file mode 100644 index 0000000..70375e8 --- /dev/null +++ b/t/class_inspector.t @@ -0,0 +1,337 @@ +# Unit testing for Class::Inspector + +# Do all the tests on ourself, where possible, as we know we will be loaded. + +use strict; +use warnings; +use Test::More tests => 56; +use Class::Inspector (); + +# To make maintaining this a little faster, +# CI is defined as Class::Inspector, and +# BAD for a class we know doesn't exist. +use constant CI => 'Class::Inspector'; +use constant BAD => 'Class::Inspector::Nonexistant'; + +# How many functions and public methods are there in Class::Inspector +my $base_functions = 18; +my $base_public = 12; +my $base_private = $base_functions - $base_public; + + + + + +##################################################################### +# Begin Tests + +# Check the good/bad class code +ok( CI->_class( CI ), 'Class validator works for known valid' ); +ok( CI->_class( BAD ), 'Class validator works for correctly formatted, but not installed' ); +ok( CI->_class( 'A::B::C::D::E' ), 'Class validator works for long classes' ); +ok( CI->_class( '::' ), 'Class validator allows main' ); +ok( CI->_class( '::Blah' ), 'Class validator works for main aliased' ); +ok( ! CI->_class(), 'Class validator failed for missing class' ); +ok( ! CI->_class( '4teen' ), 'Class validator fails for number starting class' ); +ok( ! CI->_class( 'Blah::%f' ), 'Class validator catches bad characters' ); + + + + + + +# Check the loaded method +ok( CI->loaded( CI ), "->loaded detects loaded" ); +ok( ! CI->loaded( BAD ), "->loaded detects not loaded" ); + + + + + +# Check the file name methods +my $filename = CI->filename( CI ); +ok( $filename eq File::Spec->catfile( "Class", "Inspector.pm" ), "->filename works correctly" ); +my $inc_filename = CI->_inc_filename( CI ); +ok( $inc_filename eq "Class/Inspector.pm", "->_inc_filename works correctly" ); +ok( index( CI->loaded_filename(CI), $filename ) >= 0, "->loaded_filename works" ); +ok( ($filename eq $inc_filename or index( CI->loaded_filename(CI), $inc_filename ) == -1), "->loaded_filename works" ); +ok( index( CI->resolved_filename(CI), $filename ) >= 0, "->resolved_filename works" ); +ok( ($filename eq $inc_filename or index( CI->resolved_filename(CI), $inc_filename ) == -1), "->resolved_filename works" ); + + + + + +# Check the installed stuff +ok( CI->installed( CI ), "->installed detects installed" ); +ok( ! CI->installed( BAD ), "->installed detects not installed" ) + || do { + diag "\@INC=$_" for @INC; + diag "$_=$INC{$_}" for sort keys %INC; + }; + + + + + +# Check the functions +my $functions = CI->functions( CI ); +ok( (ref($functions) eq 'ARRAY' + and $functions->[0] eq '_class' + and scalar @$functions == $base_functions), + "->functions works correctly" ); +ok( ! CI->functions( BAD ), "->functions fails correctly" ); + + + + + +# Check function refs +$functions = CI->function_refs( CI ); +ok( (ref($functions) eq 'ARRAY' + and ref $functions->[0] + and ref($functions->[0]) eq 'CODE' + and scalar @$functions == $base_functions), + "->function_refs works correctly" ); +ok( ! CI->functions( BAD ), "->function_refs fails correctly" ); + + + + + +# Check function_exists +ok( CI->function_exists( CI, 'installed' ), + "->function_exists detects function that exists" ); +ok( ! CI->function_exists( CI, 'nsfladf' ), + "->function_exists fails for bad function" ); +ok( ! CI->function_exists( CI ), + "->function_exists fails for missing function" ); +ok( ! CI->function_exists( BAD, 'function' ), + "->function_exists fails for bad class" ); + + + + + +# Check the methods method. +# First, defined a new subclass of Class::Inspector with some additional methods +CLASS: { + package Class::Inspector::Dummy; + + use strict; + BEGIN { + require Class::Inspector; + @Class::Inspector::Dummy::ISA = 'Class::Inspector'; + } + + sub _a_first { 1; } + sub adummy1 { 1; } + sub _dummy2 { 1; } + sub dummy3 { 1; } + sub installed { 1; } +} + +package main; + +my $methods = CI->methods( CI ); +ok( ( ref($methods) eq 'ARRAY' + and $methods->[0] eq '_class' + and scalar @$methods == $base_functions), + "->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq '_a_first' + and scalar @$methods == ($base_functions + 4) + and scalar( grep { /dummy/ } @$methods ) == 3), + "->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "->methods fails correctly" ); + +# Check the variety of different possible ->methods options + +# Public option +$methods = CI->methods( CI, 'public' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq 'children' + and scalar @$methods == $base_public), + "Public ->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy', 'public' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq 'adummy1' + and scalar @$methods == ($base_public + 2) + and scalar( grep { /dummy/ } @$methods ) == 2), + "Public ->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "Public ->methods fails correctly" ); + +# Private option +$methods = CI->methods( CI, 'private' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq '_class' + and scalar @$methods == $base_private), + "Private ->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy', 'private' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq '_a_first' + and scalar @$methods == ($base_private + 2) + and scalar( grep { /dummy/ } @$methods ) == 1), + "Private ->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "Private ->methods fails correctly" ); + +# Full option +$methods = CI->methods( CI, 'full' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq 'Class::Inspector::_class' + and scalar @$methods == $base_functions), + "Full ->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy', 'full' ); +ok( (ref($methods) eq 'ARRAY' + and $methods->[0] eq 'Class::Inspector::Dummy::_a_first' + and scalar @$methods == ($base_functions + 4) + and scalar( grep { /dummy/ } @$methods ) == 3), + "Full ->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "Full ->methods fails correctly" ); + +# Expanded option +$methods = CI->methods( CI, 'expanded' ); +ok( (ref($methods) eq 'ARRAY' + and ref($methods->[0]) eq 'ARRAY' + and $methods->[0]->[0] eq 'Class::Inspector::_class' + and $methods->[0]->[1] eq 'Class::Inspector' + and $methods->[0]->[2] eq '_class' + and ref($methods->[0]->[3]) eq 'CODE' + and scalar @$methods == $base_functions), + "Expanded ->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy', 'expanded' ); +ok( (ref($methods) eq 'ARRAY' + and ref($methods->[0]) eq 'ARRAY' + and $methods->[0]->[0] eq 'Class::Inspector::Dummy::_a_first' + and $methods->[0]->[1] eq 'Class::Inspector::Dummy' + and $methods->[0]->[2] eq '_a_first' + and ref($methods->[0]->[3]) eq 'CODE' + and scalar @$methods == ($base_functions + 4) + and scalar( grep { /dummy/ } map { $_->[2] } @$methods ) == 3), + "Expanded ->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "Expanded ->methods fails correctly" ); + +# Check clashing between options +ok( ! CI->methods( CI, 'public', 'private' ), "Public and private ->methods clash correctly" ); +ok( ! CI->methods( CI, 'private', 'public' ), "Public and private ->methods clash correctly" ); +ok( ! CI->methods( CI, 'full', 'expanded' ), "Full and expanded ->methods class correctly" ); +ok( ! CI->methods( CI, 'expanded', 'full' ), "Full and expanded ->methods class correctly" ); + +# Check combining options +$methods = CI->methods( CI, 'public', 'expanded' ); +ok( (ref($methods) eq 'ARRAY' + and ref($methods->[0]) eq 'ARRAY' + and $methods->[0]->[0] eq 'Class::Inspector::children' + and $methods->[0]->[1] eq 'Class::Inspector' + and $methods->[0]->[2] eq 'children' + and ref($methods->[0]->[3]) eq 'CODE' + and scalar @$methods == $base_public), + "Public + Expanded ->methods works for non-inheriting class" ); +$methods = CI->methods( 'Class::Inspector::Dummy', 'public', 'expanded' ); +ok( (ref($methods) eq 'ARRAY' + and ref($methods->[0]) eq 'ARRAY' + and $methods->[0]->[0] eq 'Class::Inspector::Dummy::adummy1' + and $methods->[0]->[1] eq 'Class::Inspector::Dummy' + and $methods->[0]->[2] eq 'adummy1' + and ref($methods->[0]->[3]) eq 'CODE' + and scalar @$methods == ($base_public + 2) + and scalar( grep { /dummy/ } map { $_->[2] } @$methods ) == 2), + "Public + Expanded ->methods works for inheriting class" ); +ok( ! CI->methods( BAD ), "Expanded ->methods fails correctly" ); + + + + + +##################################################################### +# Search Tests + +# Create the classes to use +CLASSES: { + package Foo; + + sub foo { 1 }; + + package Foo::Subclass; + + @Foo::Subclass::ISA = 'Foo'; + + package Bar; + + @Bar::ISA = 'Foo'; + + package This; + + sub isa { $_[1] eq 'Foo' ? 1 : undef } + + 1; +} + +# Check trivial ->find cases +SCOPE: { + is( CI->subclasses( '' ), undef, '->subclasses(bad) returns undef' ); + is( CI->subclasses( BAD ), '', '->subclasses(none) returns false' ); + my $rv = CI->subclasses( CI ); + is_deeply( $rv, [ 'Class::Inspector::Dummy' ], '->subclasses(CI) returns just itself' ); + + # Check non-trivial ->subclasses cases + $rv = CI->subclasses( 'Foo' ); + is_deeply( $rv, [ 'Bar', 'Foo::Subclass', 'This' ], + '->subclasses(nontrivial) returns the expected class list' ); +} + + + + + +##################################################################### +# Regression Tests + +# Discovered in 1.06, fixed in 1.07 +# In some cases, spurious empty GLOB entries can be created in a package. +# These contain no actual symbols, but were causing ->loaded to return true. +# An empty namespace with a single spurious empty glob entry (although +# created in this test with a scalar) should return FALSE for ->loaded +$Class::Inspector::SpuriousPackage::something = 1; +$Class::Inspector::SpuriousPackage::something = 1; # Avoid a warning +ok( ! Class::Inspector->loaded('Class::Inspector::SpuriousPackage'), + '->loaded returns false for spurious glob in package' ); + + + +# Discovered in 1.11, fixed in 1.12 +# With the introduction of ->subclasses, we exposed ourselves to +# non-local problems with ->isa method implementations. +PACKAGES: { + # The busted package + package Class::Inspector::BrokenISA; + use vars qw{&isa}; + our $VERSION = '0.01'; + # The test packages + package My::Foo; + our $VERSION = '0.01'; + package My::Bar; + our $VERSION = '0.01'; + use base qw( My::Foo ); +} +TESTS: { + my $rv = Class::Inspector->subclasses( 'My::Foo' ); + is_deeply( $rv, [ 'My::Bar' ], + '->subclasses in the presence of an evil ->isa does not crash' ); +} + + +{ + { + package Baz; + + our @ISA = qw( ::foo bar'baz ); ## no critic + } + + is_deeply \@Baz::ISA, [qw( ::foo bar'baz )]; + + Class::Inspector->methods('Baz'); + + is_deeply \@Baz::ISA, [qw( ::foo bar'baz )]; +} diff --git a/t/class_inspector__devel_hide.t b/t/class_inspector__devel_hide.t new file mode 100644 index 0000000..9789709 --- /dev/null +++ b/t/class_inspector__devel_hide.t @@ -0,0 +1,11 @@ +use strict; +use warnings; +use Test::More; +use Class::Inspector; + +eval { require Devel::Hide }; +plan skip_all => 'test requires Devel::Hide' if $@; +plan tests => 2; + +ok( Class::Inspector->installed('Class::Inspector') ); +ok( ! Class::Inspector->installed('Class::Inspector::Bogus') ); diff --git a/t/class_inspector__inc_to_local.t b/t/class_inspector__inc_to_local.t new file mode 100644 index 0000000..f44c60e --- /dev/null +++ b/t/class_inspector__inc_to_local.t @@ -0,0 +1,31 @@ +# Unit testing for Class::Inspector + +# Do all the tests on ourself, where possible, as we know we will be loaded. + +use strict; +use warnings; +use Test::More tests => 2; +use Class::Inspector (); + + + + +##################################################################### +# Try the simplistic Win32 approach + +SKIP: { + skip( "Skipping Win32 test", 1 ) unless $^O eq 'MSWin32'; + my $inc = 'C:/foo/bar.pm'; + my $local = Class::Inspector->_inc_to_local($inc); + is( $local, 'C:\foo\bar.pm', '->_inc_to_local ok' ); +} + + + + + +##################################################################### +# More general tests + +my $module = Class::Inspector->_inc_to_local($INC{'Class/Inspector.pm'}); +ok( -f $module, 'Found ourself' ); diff --git a/t/class_inspector_functions.t b/t/class_inspector_functions.t new file mode 100644 index 0000000..3672085 --- /dev/null +++ b/t/class_inspector_functions.t @@ -0,0 +1,118 @@ +# Reproduce some of the unit tests in the main unit tests +# of the method interface, but not all. This makes the maintenance +# slightly less annoying. + +use strict; +use warnings; +use Test::More tests => 24; +use Class::Inspector::Functions; + +# To make maintaining this a little faster, +# CI is defined as Class::Inspector, and +# BAD for a class we know doesn't exist. +use constant CI => 'Class::Inspector'; +use constant BAD => 'Class::Inspector::Nonexistant'; + +my @exported_functions = qw( + installed + loaded + filename + functions + methods + subclasses +); + +my @exportok_functions = qw( + loaded_filename + function_refs + function_exists +); + +##################################################################### +# Begin Tests + +# check the export lists: +foreach my $function (@exported_functions) { + ok( main->can($function), "exported function '$function' was found" ); +} + +foreach my $function (@exportok_functions) { + ok( ! main->can($function), "optionally exported function '$function' was not found" ); +} + +Class::Inspector::Functions->import(':ALL'); + +foreach my $function (@exportok_functions) { + ok( main->can($function), "optionally exported function '$function' was found after full import" ); +} + + + +# Check the loaded function +ok( loaded( CI ), "loaded detects loaded" ); +ok( ! loaded( BAD ), "loaded detects not loaded" ); + +# Check the file name functions +my $filename = filename( CI ); +ok( $filename eq File::Spec->catfile( "Class", "Inspector.pm" ), "filename works correctly" ); +ok( index( loaded_filename(CI), $filename ) >= 0, "loaded_filename works" ); +my $inc_filename = CI->_inc_filename( CI ); +ok( ($filename eq $inc_filename or index( loaded_filename(CI), $inc_filename ) == -1), "loaded_filename works" ); +ok( index( resolved_filename(CI), $filename ) >= 0, "resolved_filename works" ); +ok( ($filename eq $inc_filename or index( resolved_filename(CI), $inc_filename ) == -1), "resolved_filename works" ); + +unshift @INC, sub { + my $coderef = shift; + my $filename = shift; + + if ($filename eq 'Foo/Bar.pm') { + open my $fh, '<', __FILE__; + return (undef, $fh); + } + return +}; + +unshift @INC, [ sub { + my $arrayref = shift; + my $filename = shift; + + die "args wrong" unless + ref($arrayref->[0]) eq 'CODE' + && $arrayref->[1] == 1 + && $arrayref->[2] == 2 + && $arrayref->[3] == 3; + + if($filename eq 'Foo/Baz.pm') { + open my $fh, '<', __FILE__; + return $fh; + } + return +}, 1,2,3]; + +unshift @INC, MyHook->new; + +# Check the installed stuff +ok( installed( CI ), "installed detects installed" ); +ok( ! installed( BAD ), "installed detects not installed" ); +ok( installed( 'Foo::Bar'), "installed detects coderef installed" ); +ok( installed( 'Foo::Baz'), "installed detects arrayref installed" ); +ok( installed( 'Foo::Foo'), "installed detects object installed" ); + +package + MyHook; + +sub new { + my($class) = @_; + bless {}, $class; +} + +sub MyHook::INC { + my($self, $filename) = @_; + die "self wrong" unless ref $self eq 'MyHook'; + + if($filename eq 'Foo/Foo.pm') { + open my $fh, '<', __FILE__; + return $fh; + } + return (); +} diff --git a/xt/author/critic.t b/xt/author/critic.t new file mode 100644 index 0000000..7af1e67 --- /dev/null +++ b/xt/author/critic.t @@ -0,0 +1,22 @@ +use strict; +use warnings; +use Test::More; + +BEGIN { + if(eval { require Test2::Tools::PerlCritic; 1 }) + { + Test2::Tools::PerlCritic->import; + } + else + { + plan skip_all => 'Test requires Test2::Tools::PerlCritic'; + } +} + +my $critic = Perl::Critic->new( + -profile => 'perlcriticrc', +); + +perl_critic_ok ['lib','t'], $critic; + +done_testing; diff --git a/xt/author/eol.t b/xt/author/eol.t new file mode 100644 index 0000000..21395b5 --- /dev/null +++ b/xt/author/eol.t @@ -0,0 +1,16 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::EOL' + unless eval q{ use Test::EOL; 1 }; +}; +use Test::EOL; +use FindBin; +use File::Spec; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +all_perl_files_ok(grep { -e $_ } qw( bin lib t Makefile.PL )); + + diff --git a/xt/author/no_tabs.t b/xt/author/no_tabs.t new file mode 100644 index 0000000..d62d42f --- /dev/null +++ b/xt/author/no_tabs.t @@ -0,0 +1,16 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::NoTabs' + unless eval q{ use Test::NoTabs; 1 }; +}; +use Test::NoTabs; +use FindBin; +use File::Spec; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +all_perl_files_ok( grep { -e $_ } qw( bin lib t Makefile.PL )); + + diff --git a/xt/author/pod.t b/xt/author/pod.t new file mode 100644 index 0000000..282ed97 --- /dev/null +++ b/xt/author/pod.t @@ -0,0 +1,16 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::Pod' + unless eval q{ use Test::Pod; 1 }; +}; +use Test::Pod; +use FindBin; +use File::Spec; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +all_pod_files_ok( grep { -e $_ } qw( bin lib )); + + diff --git a/xt/author/pod_coverage.t b/xt/author/pod_coverage.t new file mode 100644 index 0000000..bd70549 --- /dev/null +++ b/xt/author/pod_coverage.t @@ -0,0 +1,75 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires 5.010 or better' + unless $] >= 5.010; + plan skip_all => 'test requires Test::Pod::Coverage' + unless eval q{ use Test::Pod::Coverage; 1 }; + plan skip_all => 'test requires YAML' + unless eval q{ use YAML; 1; }; +}; +use Test::Pod::Coverage; +use YAML qw( LoadFile ); +use FindBin; +use File::Spec; + +my $config_filename = File::Spec->catfile( + $FindBin::Bin, File::Spec->updir, File::Spec->updir, 'author.yml' +); + +my $config; +$config = LoadFile($config_filename) + if -r $config_filename; + +plan skip_all => 'disabled' if $config->{pod_coverage}->{skip}; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +my @private_classes; +my %private_methods; + +push @{ $config->{pod_coverage}->{private} }, + 'Alien::.*::Install::Files#Inline'; + +foreach my $private (@{ $config->{pod_coverage}->{private} }) +{ + my($class,$method) = split /#/, $private; + if(defined $class && $class ne '') + { + my $regex = eval 'qr{^' . $class . '$}'; + if(defined $method && $method ne '') + { + push @private_classes, { regex => $regex, method => $method }; + } + else + { + push @private_classes, { regex => $regex, all => 1 }; + } + } + elsif(defined $method && $method ne '') + { + $private_methods{$_} = 1 for split /,/, $method; + } +} + +my @classes = all_modules; + +plan tests => scalar @classes; + +foreach my $class (@classes) +{ + SKIP: { + my($is_private_class) = map { 1 } grep { $class =~ $_->{regex} && $_->{all} } @private_classes; + skip "private class: $class", 1 if $is_private_class; + + my %methods = map {; $_ => 1 } map { split /,/, $_->{method} } grep { $class =~ $_->{regex} } @private_classes; + $methods{$_} = 1 for keys %private_methods; + + my $also_private = eval 'qr{^' . join('|', keys %methods ) . '$}'; + + pod_coverage_ok $class, { also_private => [$also_private] }; + }; +} + + diff --git a/xt/author/pod_spelling_common.t b/xt/author/pod_spelling_common.t new file mode 100644 index 0000000..75b9f8d --- /dev/null +++ b/xt/author/pod_spelling_common.t @@ -0,0 +1,29 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::Pod::Spelling::CommonMistakes' + unless eval q{ use Test::Pod::Spelling::CommonMistakes; 1 }; + plan skip_all => 'test requires YAML' + unless eval q{ use YAML qw( LoadFile ); 1 }; +}; +use Test::Pod::Spelling::CommonMistakes; +use FindBin; +use File::Spec; + +my $config_filename = File::Spec->catfile( + $FindBin::Bin, File::Spec->updir, File::Spec->updir, 'author.yml' +); + +my $config; +$config = LoadFile($config_filename) + if -r $config_filename; + +plan skip_all => 'disabled' if $config->{pod_spelling_common}->{skip}; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +# TODO: test files in bin too. +all_pod_files_ok; + + diff --git a/xt/author/strict.t b/xt/author/strict.t new file mode 100644 index 0000000..37aea42 --- /dev/null +++ b/xt/author/strict.t @@ -0,0 +1,23 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::Strict' + unless eval q{ use Test::Strict; 1 }; +}; +use Test::Strict; +use FindBin; +use File::Spec; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +unshift @Test::Strict::MODULES_ENABLING_STRICT, + 'ozo', + 'Test2::Bundle::SIPS', + 'Test2::V0', + 'Test2::Bundle::Extended'; +note "enabling strict = $_" for @Test::Strict::MODULES_ENABLING_STRICT; + +all_perl_files_ok( grep { -e $_ } qw( bin lib t Makefile.PL )); + + diff --git a/xt/author/version.t b/xt/author/version.t new file mode 100644 index 0000000..153e334 --- /dev/null +++ b/xt/author/version.t @@ -0,0 +1,39 @@ +use strict; +use warnings; +use Test::More; +use FindBin (); +BEGIN { + + plan skip_all => "test requires Test::Version 2.00" + unless eval q{ + use Test::Version 2.00 qw( version_all_ok ), { + has_version => 1, + filename_match => sub { $_[0] !~ m{/(ConfigData|Install/Files)\.pm$} }, + }; + 1 + }; + + plan skip_all => 'test requires YAML' + unless eval q{ use YAML; 1; }; +} + +use YAML qw( LoadFile ); +use FindBin; +use File::Spec; + +my $config_filename = File::Spec->catfile( + $FindBin::Bin, File::Spec->updir, File::Spec->updir, 'author.yml' +); + +my $config; +$config = LoadFile($config_filename) + if -r $config_filename; + +if($config->{version}->{dir}) +{ + note "using dir " . $config->{version}->{dir} +} + +version_all_ok($config->{version}->{dir} ? ($config->{version}->{dir}) : ()); +done_testing; + diff --git a/xt/release/fixme.t b/xt/release/fixme.t new file mode 100644 index 0000000..73bae2c --- /dev/null +++ b/xt/release/fixme.t @@ -0,0 +1,20 @@ +use strict; +use warnings; +use Test::More; +BEGIN { + plan skip_all => 'test requires Test::Fixme' + unless eval q{ use Test::Fixme 0.14; 1 }; +}; +use Test::Fixme 0.07; +use FindBin; +use File::Spec; + +chdir(File::Spec->catdir($FindBin::Bin, File::Spec->updir, File::Spec->updir)); + +run_tests( + match => qr/FIXME/, + where => [ grep { -e $_ } qw( bin lib t Makefile.PL Build.PL )], + warn => 1, +); + +