commit ef5c33623c5ce4671454840475a2df20ca247c28 Author: denghao Date: Thu Sep 8 12:02:06 2022 +0300 Import Upstream version 2.28 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0cdd6bb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: perl +perl: + - "5.26" + - "5.24" + - "5.22" + - "5.20" + - "5.18" + - "5.16" + - "5.14" + - "5.12" + - "5.10" + - "5.8" diff --git a/Changelog.ini b/Changelog.ini new file mode 100644 index 0000000..4b24d93 --- /dev/null +++ b/Changelog.ini @@ -0,0 +1,328 @@ +[Module] +Name=Config-Tiny: +Changelog.Creator=Module::Metadata::Changes V 2.12 +Changelog.Parser=Config::IniFiles V 3.000003 + +[V 2.28] +Date=2022-01-04T15:47:58 +Comments= < $VERSION in Makefile.PL to VERSION_FROM => 'lib/Config/Tiny.pm'. +Reported by Jean-Louis Morel. See RT#88670. +EOT + +[V 2.18] +Date=2013-09-14T10:03:00 +Comments= < newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); +- Rename t/*.t files. I use '.' rather than '_' in file names because the latter is a shift +char. +- Add MANIFEST.SKIP, Changelog.ini, Build.PL, META.json. +- Add an FAQ to the docs. +- Clean up the docs. +EOT + +[V 2.14] +Date=2011-03-24T12:00:00 +Comments= <write() it was erroneous +- Removed dependency on Fcntl +- Added the read_string() method +- Other minor tweaks to shrink the code +EOT + +[V 0.3] +Date=2002-12-09T00:44:21 +Comments= < $VERSION in Makefile.PL to VERSION_FROM => 'lib/Config/Tiny.pm'. + Reported by Jean-Louis Morel. See RT#88670. + +2.18 2013-09-14T10:03:00 + - Remove obsolete and wrong version # from Makefile.PL. + Reported by Jean-Louis Morel. See RT#88658. + - Implement Kevin Ryde's suggestion to test if read() will return undef. + If so, set an error message and (still) return undef. + +2.17 2013-09-13T12:41:00 + - Remove the file tests -efr during calls to read(). The open() tests for any error. + Also, the -f test was reporting /dev/null as a directory, not a file. + Thanx to Kevin Ryde for pushing me to implement this. See RT#36974. + - Clean up some error messages slightly. + +2.16 2013-09-06T11:54:00 + - Replace Path::Tiny with File::Spec, because the former's list of dependencies is so long :-(. + Changed files: t/02.main.t, t/04.utf8.t, Build.PL and Makefile.PL. + See: RT#88435 (for Tree::DAG_Node) for an explanation. + +2.15 2013-08-04T14:59:00 + - Clean up the shambolic dates in this file. + - Add a note under Caveats about setting options more that once. Only the first case is + respected. Thanx to Kimmel K. See RT#69795. + - Add a $encoding parameter to read_file() and write_file(). See docs for details. + Add t/04.utf8.t and t/04.utf8.txt. + Thanx to Mark Lawrence and Wolfgang Husmann. See RT#71029 and RT#85571. + - For BSD-based systems, when writing a file during tests, use: + my($temp_dir) = File::Temp -> newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); + - Rename t/*.t files. I use '.' rather than '_' in file names because the latter is a shift + char. + - Add MANIFEST.SKIP, Changelog.ini, Build.PL, META.json. + - Add an FAQ to the docs. + - Clean up the docs. + +2.14 Thu Mar 24 12:00:00 2011 + - Resolved #63080: module can write multiline values but not read them + - Removed -w from tests to allow tests with tainting on + +2.13 Fri Sep 3 12:00:00 2010 + - Resolved #60703: Display glitch in Config::Tiny 2.12 POD + - Resolved #40585: member 'set;' doesn't exist + - Resolved #30479: does not warn or die when writing data it + cannot later. + +2.12 Thu Nov 1 12:00:00 2007 + - Converting build script from Module::Install to tinier EU:MM + +2.10 Sat Sep 20 12:00:00 2006 + - This release contains only build-time changes + - Did a little housekeeping on Makefile.PL and the unit tests + - Upgrading to Module::Install 0.64 + +2.09 Sat Jul 15 12:00:00 2006 + - This release contains only build-time changes + - Added a dependency on ExtUtils::MakeMaker 6.11 + Module::Install may have an issue with older EU:MM installs + +2.08 Sat 15 Jul 12:00:00 2006 + - This release contains only build-time changes + - Upgraded to Module::Install 0.63 + +2.07 Wed May 10 12:00:00 2006 + - This release contains only build-time changes + - AutoInstall is only needed for options, so remove auto_install + +2.06 Sun Apr 23 12:00:00 2006 + - No functional changes. + - Moved test.conf to the root dir, removing last use of File::Spec + - It also means we don't need FindBin, so removed that too + - Upgrading to Module::Install 0.62 + +2.05 Thu Feb 23 12:00:00 2006 + - No functional changes. + - Moved over from the old CVS repository to the new SVN one + - Updated tests for the new release system + - Upgrading to a newer Module::Install + +2.04 Sat Dec 31 12:00:00 2005 + - No functional changes. + - Upgrading to a newer Module::Install to address Cygwin problem + +2.03 Fri Dec 30 12:00:00 2005 + - No functional changes. + - POD Change: CPAN #15143 Clear things up about $! after + unsuccessful read()? (flatworm) + - Upgraded Makefile.PL to use Module::Install + +2.02 Sun Jun 19 12:00:00 2005 + - Add trimming of whitespace from the section names so that we can + use section tags like [ section ] and have it Do What You Mean. + - Cleaned up the POD a little more. + +2.01 Thu Mar 24 12:00:00 2005 + - Lars Thegler noted in CSS::Tiny that 3-argument open is not + supported by 5.005. Added a small fix to change it to 2-argument + open. + +2.00 Fri Jul 16 12:00:00 2004 + - Final tweaks to round out complete 5.004 and Win32 compatibility + +1.9 Wed Jul 7 12:00:00 2004 + - Applied some small optimisations from Japheth Cleaver + +1.8 Wed Jun 30 12:00:00 2004 + - Fixed a bug whereby trying to load an empty file returned an error, + when it should be valid (if an empty object) + +1.7 Tue Jun 22 12:00:00 2004 + - Added a little more flexibility in the 'read' and 'read_string' methods + to handle being called in unexpected, but recoverable, ways. + +1.6 Mon Mar 1 12:00:00 2004 + - Bug fix: Sections without keys didn't appear at all in the parsed struct + +1.5 Wed Jan 7 12:00:00 2004 + - Updating documentation to provide a correct location to send bug reports + +1.4 Wed Dec 24 12:00:00 2003 + - Caught a warning when trying to parse an undefined string. + Returns undef in that case. + - Merry Christmas and a productive New Year to you all! + +1.3 Fri Nov 7 12:00:00 2003 + - Slightly altered a regex so that trailing whitespace in properties + is dropped. + +1.2 Wed Aug 12 15:51:12 2003 + - Applied a variety of small changed designed to reduce the number of + opcodes generated, without changing the functionality. + This should save a few K in load overhead. + +1.1 Wed Apr 23 22:56:21 2003 + - When reporting a bad line, put single quotes around the + lines contents in the error message. + - Small updates to the pod documentation + +1.0 Sat Dec 21 11:53:51 2002 + - Removed file locking, since we read/write virtually atomically now + - Removed mode support from ->write() it was erroneous + - Removed dependency on Fcntl + - Added the read_string() method + - Other minor tweaks to shrink the code + +0.3 Mon Dec 09 00:44:21 2002 + - Upgraded tests to Test::More, to deep test the structs + - Added Fcntl to the required modules + +0.2 Tue Nov 26 21:51:34 2002 + - Don't import Fcntl symbols + +0.1 Wed Nov 13 16:50:23 2002 + - original version \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d223c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,378 @@ +Terms of Perl 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 General Public License (GPL) +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc., +51 Franklin Street, 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 licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU 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. This General Public License applies to most of +the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you wish), 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 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 show +them these terms so they know 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. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually obtain +patent licenses, in effect making the program proprietary. To prevent this, we +have made it clear that any patent must be licensed for everyone's free use or +not licensed at all. + +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 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 +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or translated +into another language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is not +restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +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 License and to the absence of any +warranty; and give any other recipients of the Program a copy of this License +along with the Program. + +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. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such +modifications or work under the terms of Section 1 above, provided that you also +meet all of these conditions: + +a) You must cause the modified files to carry prominent notices stating that you +changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in +part contains or is derived from the Program or any part thereof, to be licensed +as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you +must cause it, when started running for such interactive use in the most ordinary +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 License. (Exception: if the +Program itself is interactive but does not normally print such an announcement, +your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Program, the distribution of the whole must be on +the terms of this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to +work written entirely by you; rather, the intent is to exercise the right to control +the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 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 Sections 1 and 2 above on a +medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any +third party, for a charge no more than your cost of physically performing source +distribution, a complete machine-readable copy of the corresponding source +code, to be distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute +corresponding source code. (This alternative is allowed only for noncommercial +distribution and only if you received the program in object code or executable +form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all the +source code for all modules it contains, plus any associated interface definition +files, plus the scripts used to control compilation and installation of the +executable. However, as a special exception, the source code distributed need +not include anything that is normally distributed (in either source or binary form) +with the major components (compiler, kernel, and so on) of the operating system +on which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so long +as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not accept +this License. Therefore, by modifying or distributing 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 for copying, distributing or modifying the +Program or works based on it. + +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. You are not responsible for enforcing compliance by third parties +to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed on +you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of this +License. If you cannot distribute so as to satisfy simultaneously your obligations +under this License and any other pertinent obligations, then as a consequence +you may not distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who receive +copies directly or indirectly through you, then the only way you could satisfy +both it and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under any particular +circumstance, the balance of the section is intended to apply and the section as +a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other +property right claims or to contest validity of any such claims; this section has +the sole purpose of protecting the integrity of the free software distribution +system, which is implemented by public license practices. Many people have +made generous contributions to the wide range of software distributed through +that system in reliance on consistent application of that system; it is up to the +author/donor to decide if he or she is willing to distribute software through any +other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries +either by patents or by copyrighted interfaces, the original copyright holder who +places the Program under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is permitted +only in or among countries not thus excluded. In such case, this License +incorporates the limitation as if written in the body of this License. + +9. 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 this 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 this License, you may choose any version ever +published by the Free Software Foundation. + +10. 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 + +11. 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. + +12. 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 + + +---------------------------------------------------------------------------- + +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..a653303 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,27 @@ +.travis.yml +Changelog.ini +Changes +lib/Config/Tiny.pm +LICENSE +Makefile.PL +MANIFEST This list of files +MANIFEST.SKIP +README +t/0 +t/00.versions.t +t/00.versions.tx +t/01.compile.t +t/02.main.t +t/03.read.string.t +t/04.utf8.t +t/04.utf8.txt +t/05.zero.t +t/06.repeat.key.t +t/07.trailing.comment.t +t/08.constructor.t +t/test.conf +xt/author/pod.t +xt/meta.t +xt/pmv.t +META.yml Module YAML meta-data (added by MakeMaker) +META.json Module JSON meta-data (added by MakeMaker) diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..b19ed41 --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,47 @@ +# Avoid version control files. +,v$ +\B\.cvsignore$ +\B\.git\b +\B\.gitignore\b +\B\.svn\b +\bCVS\b +\bRCS\b + +# Avoid Makemaker generated and utility files. +\bblib +\bblibdirs$ +\bpm_to_blib$ +\bMakefile$ +\bMakeMaker-\d + +# Avoid Module::Build generated and utility files. +\b_build +\bBuild$ +\bBuild.bat$ + +# Avoid Devel::Cover generated files +\bcover_db + +# Avoid temp and backup files. +~$ +\#$ +\.# +\.bak$ +\.old$ +\.rej$ +\.tmp$ + +# Avoid OS-specific files/dirs +# Mac OSX metadata +\B\.DS_Store +# Mac OSX SMB mount metadata files +\B\._ + +# Avoid UltraEdit files. +\.prj$ +\.pui$ + +^MYMETA.yml$ +^MYMETA\.json$ + +^Config-Tiny-.* diff --git a/META.json b/META.json new file mode 100644 index 0000000..3d203d0 --- /dev/null +++ b/META.json @@ -0,0 +1,65 @@ +{ + "abstract" : "Read/Write .ini style files with as little code as possible", + "author" : [ + "Adam Kennedy " + ], + "dynamic_config" : 1, + "generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : 2 + }, + "name" : "Config-Tiny", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : { + "requires" : { + "Test::More" : "0.47" + } + }, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0" + } + }, + "runtime" : { + "requires" : { + "File::Spec" : "3.30", + "File::Temp" : "0.22", + "perl" : "5.008001", + "strict" : "0", + "utf8" : "0" + } + }, + "test" : { + "requires" : { + "Test::More" : "1.001002", + "Test::Pod" : "1.51" + } + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "web" : "https://github.com/ronsavage/Config-Tiny/issues" + }, + "license" : [ + "http://opensource.org/licenses/Perl" + ], + "repository" : { + "type" : "git", + "url" : "https://github.com/ronsavage/Config-Tiny.git", + "web" : "https://github.com/ronsavage/Config-Tiny" + } + }, + "version" : "2.28", + "x_serialization_backend" : "JSON::PP version 4.06" +} diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..d2b39f5 --- /dev/null +++ b/META.yml @@ -0,0 +1,32 @@ +--- +abstract: 'Read/Write .ini style files with as little code as possible' +author: + - 'Adam Kennedy ' +build_requires: + Test::More: '1.001002' + Test::Pod: '1.51' +configure_requires: + ExtUtils::MakeMaker: '0' +dynamic_config: 1 +generated_by: 'ExtUtils::MakeMaker version 7.62, 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: Config-Tiny +no_index: + directory: + - t + - inc +requires: + File::Spec: '3.30' + File::Temp: '0.22' + perl: '5.008001' + strict: '0' + utf8: '0' +resources: + bugtracker: https://github.com/ronsavage/Config-Tiny/issues + license: http://opensource.org/licenses/Perl + repository: https://github.com/ronsavage/Config-Tiny.git +version: '2.28' +x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..f930d9b --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,83 @@ +use strict; +use warnings; + +use ExtUtils::MakeMaker; + +# ---------------------- + +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + +my(%params) = +( + ("$]" >= '5.005') ? + ( + AUTHOR => 'Adam Kennedy ', + ABSTRACT => 'Read/Write .ini style files with as little code as possible', + ) : (), + clean => + { + FILES => 'blib/* Makefile MANIFEST Config-Tiny-*', + }, + dist => + { + COMPRESS => 'gzip', + SUFFIX => 'gz', + }, + BUILD_REQUIRES => + { + # Skip on Windows to avoid breaking ActivePerl PPMs + # 0.47 means 5.6.2 or newer, which everyone on Win32 has. + ($^O eq 'MSWin32' ? () : ('Test::More' => '0.47') ), + }, + DISTNAME => 'Config-Tiny', + EXE_FILES => [], + MIN_PERL_VERSION => '5.008001', + NAME => 'Config::Tiny', + PL_FILES => {}, + PREREQ_PM => + { + 'File::Spec' => '3.30', + 'File::Temp' => '0.22', + 'strict' => '0', + 'utf8' => '0', + }, + TEST_REQUIRES => + { + 'Test::More' => '1.001002', + 'Test::Pod' => '1.51', + }, + VERSION_FROM => 'lib/Config/Tiny.pm', +); + +if (eval{ExtUtils::MakeMaker->VERSION('6.30')}) +{ + $params{LICENSE} = 'perl'; +} + +if (eval{ExtUtils::MakeMaker->VERSION('6.46')}) +{ + $params{META_MERGE} = + { + 'meta-spec' => + { + version => 2, + }, + resources => + { + bugtracker => + { + web => 'https://github.com/ronsavage/Config-Tiny/issues', + }, + license => 'http://opensource.org/licenses/Perl', + repository => + { + type => 'git', + url => 'https://github.com/ronsavage/Config-Tiny.git', + web => 'https://github.com/ronsavage/Config-Tiny', + }, + }, + }; +} + +WriteMakefile(%params); diff --git a/README b/README new file mode 100644 index 0000000..8f15de3 --- /dev/null +++ b/README @@ -0,0 +1,49 @@ +README file for Config::Tiny. + +See also: Changes and Changelog.ini. + +Warning: WinZip 8.1 and 9.0 both contain an 'accidental' bug which stops +them recognizing POSIX-style directory structures in valid tar files. +You are better off using a reliable tool such as InfoZip: +ftp://ftp.info-zip.org/pub/infozip/ + +1 Installing from a Unix-like distro +------------------------------------ +shell>gunzip Config-Tiny-2.20.tgz +shell>tar xvf Config-Tiny-2.20.tar + +On Unix-like systems, assuming you have installed Module::Build V 0.25+: + +shell>perl Build.PL +shell>./Build +shell>./Build test +shell>./Build install + +On MS Windows-like systems, assuming you have installed Module::Build V 0.25+: + +shell>perl Build.PL +shell>perl Build +shell>perl Build test +shell>perl Build install + +Alternately, without Module::Build, you do this: + +Note: 'make' on MS Windows-like systems may be called 'nmake' or 'dmake'. + +shell>perl Makefile.PL +shell>make +shell>make test +shell>su (for Unix-like systems) +shell>make install +shell>exit (for Unix-like systems) + +On all systems: + +Run Tiny.pm through your favourite pod2html translator. + +2 Installing from an ActiveState distro +--------------------------------------- +shell>unzip Config-Tiny-2.20.zip +shell>ppm install --location=. Config-Tiny +shell>del Config-Tiny-2.20.ppd +shell>del PPM-Config-Tiny-2.20.tar.gz diff --git a/lib/Config/Tiny.pm b/lib/Config/Tiny.pm new file mode 100644 index 0000000..54776b4 --- /dev/null +++ b/lib/Config/Tiny.pm @@ -0,0 +1,491 @@ +package Config::Tiny; + +# If you thought Config::Simple was small... + +use strict; +use 5.008001; # For the utf8 stuff. + +# Warning: There is another version line, in t/02.main.t. + +our $VERSION = '2.28'; + +BEGIN { + $Config::Tiny::errstr = ''; +} + +# Create an object. + +sub new { return bless defined $_[1] ? $_[1] : {}, $_[0] } + +# Create an object from a file. + +sub read +{ + my($class) = ref $_[0] ? ref shift : shift; + my($file, $encoding) = @_; + + return $class -> _error('No file name provided') if (! defined $file || ($file eq '') ); + + # Slurp in the file. + + $encoding = $encoding ? "<:$encoding" : '<'; + local $/ = undef; + + open(my $CFG, $encoding, $file) or return $class -> _error( "Failed to open file '$file' for reading: $!" ); + my $contents = <$CFG>; + close($CFG ); + + return $class -> _error("Reading from '$file' returned undef") if (! defined $contents); + + return $class -> read_string( $contents ); + +} # End of read. + +# Create an object from a string. + +sub read_string +{ + my($class) = ref $_[0] ? ref shift : shift; + my($self) = bless {}, $class; + + return undef unless defined $_[0]; + + # Parse the file. + + my $ns = '_'; + my $counter = 0; + + foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift ) + { + $counter++; + + # Skip comments and empty lines. + + next if /^\s*(?:\#|\;|$)/; + + # Remove inline comments. + + s/\s\;\s.+$//g; + + # Handle section headers. + + if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) + { + # Create the sub-hash if it doesn't exist. + # Without this sections without keys will not + # appear at all in the completed struct. + + $self->{$ns = $1} ||= {}; + + next; + } + + # Handle properties. + + if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) + { + $self->{$ns}->{$1} = $2; + + next; + } + + return $self -> _error( "Syntax error at line $counter: '$_'" ); + } + + return $self; +} + +# Save an object to a file. + +sub write +{ + my($self) = shift; + my($file, $encoding) = @_; + + return $self -> _error('No file name provided') if (! defined $file or ($file eq '') ); + + $encoding = $encoding ? ">:$encoding" : '>'; + + # Write it to the file. + + my($string) = $self->write_string; + + return undef unless defined $string; + + open(my $CFG, $encoding, $file) or return $self->_error("Failed to open file '$file' for writing: $!"); + print $CFG $string; + close($CFG); + + return 1; + +} # End of write. + +# Save an object to a string. + +sub write_string +{ + my($self) = shift; + my($contents) = ''; + + for my $section ( sort { (($b eq '_') <=> ($a eq '_')) || ($a cmp $b) } keys %$self ) + { + # Check for several known-bad situations with the section + # 1. Leading whitespace + # 2. Trailing whitespace + # 3. Newlines in section name. + + return $self->_error("Illegal whitespace in section name '$section'") if $section =~ /(?:^\s|\n|\s$)/s; + + my $block = $self->{$section}; + $contents .= "\n" if length $contents; + $contents .= "[$section]\n" unless $section eq '_'; + + for my $property ( sort keys %$block ) + { + return $self->_error("Illegal newlines in property '$section.$property'") if $block->{$property} =~ /(?:\012|\015)/s; + + $contents .= "$property=$block->{$property}\n"; + } + } + + return $contents; + +} # End of write_string. + +# Error handling. + +sub errstr { $Config::Tiny::errstr } +sub _error { $Config::Tiny::errstr = $_[1]; undef } + +1; + +__END__ + +=pod + +=head1 NAME + +Config::Tiny - Read/Write .ini style files with as little code as possible + +=head1 SYNOPSIS + + # In your configuration file + rootproperty=blah + + [section] + one=twp + three= four + Foo =Bar + empty= + + # In your program + use Config::Tiny; + + # Create an empty config + my $Config = Config::Tiny->new; + + # Create a config with data + my $config = Config::Tiny->new({ + _ => { rootproperty => "Bar" }, + section => { one => "value", Foo => 42 } }); + + # Open the config + $Config = Config::Tiny->read( 'file.conf' ); + $Config = Config::Tiny->read( 'file.conf', 'utf8' ); # Neither ':' nor '<:' prefix! + $Config = Config::Tiny->read( 'file.conf', 'encoding(iso-8859-1)'); + + # Reading properties + my $rootproperty = $Config->{_}->{rootproperty}; + my $one = $Config->{section}->{one}; + my $Foo = $Config->{section}->{Foo}; + + # Changing data + $Config->{newsection} = { this => 'that' }; # Add a section + $Config->{section}->{Foo} = 'Not Bar!'; # Change a value + delete $Config->{_}; # Delete a value or section + + # Save a config + $Config->write( 'file.conf' ); + $Config->write( 'file.conf', 'utf8' ); # Neither ':' nor '>:' prefix! + + # Shortcuts + my($rootproperty) = $$Config{_}{rootproperty}; + + my($config) = Config::Tiny -> read_string('alpha=bet'); + my($value) = $$config{_}{alpha}; # $value is 'bet'. + + my($config) = Config::Tiny -> read_string("[init]\nalpha=bet"); + my($value) = $$config{init}{alpha}; # $value is 'bet'. + +=head1 DESCRIPTION + +C is a Perl class to read and write .ini style configuration +files with as little code as possible, reducing load time and memory overhead. + +Most of the time it is accepted that Perl applications use a lot of memory and modules. + +The C<*::Tiny> family of modules is specifically intended to provide an ultralight alternative +to the standard modules. + +This module is primarily for reading human written files, and anything we write shouldn't need to +have documentation/comments. If you need something with more power move up to L, +L or one of the many other C modules. + +Lastly, L does B preserve your comments, whitespace, or the order of your config +file. + +See L (and possibly others) for the preservation of the order of the entries +in the file. + +=head1 CONFIGURATION FILE SYNTAX + +Files are the same format as for MS Windows C<*.ini> files. For example: + + [section] + var1=value1 + var2=value2 + +If a property is outside of a section at the beginning of a file, it will +be assigned to the C<"root section">, available at C<$Config-E{_}>. + +Lines starting with C<'#'> or C<';'> are considered comments and ignored, +as are blank lines. + +When writing back to the config file, all comments, custom whitespace, +and the ordering of your config file elements are discarded. If you need +to keep the human elements of a config when writing back, upgrade to +something better, this module is not for you. + +=head1 METHODS + +=head2 errstr() + +Returns a string representing the most recent error, or the empty string. + +You can also retrieve the error message from the C<$Config::Tiny::errstr> variable. + +=head2 new([$config]) + +Here, the [] indicate an optional parameter. + +The constructor C creates and returns a C object. + +This will normally be a new, empty configuration, but you may also pass a +hashref here which will be turned into an object of this class. This hashref +should have a structure suitable for a configuration file, that is, a hash of +hashes where the key C<_> is treated specially as the root section. + +=head2 read($filename, [$encoding]) + +Here, the [] indicate an optional parameter. + +The C constructor reads a config file, $filename, and returns a new +C object containing the properties in the file. + +$encoding may be used to indicate the encoding of the file, e.g. 'utf8' or 'encoding(iso-8859-1)'. + +Do not add a prefix to $encoding, such as '<' or '<:'. + +Returns the object on success, or C on error. + +When C fails, C sets an error message internally +you can recover via Cerrstr>. Although in B +cases a failed C will also set the operating system error +variable C<$!>, not all errors do and you should not rely on using +the C<$!> variable. + +See t/04.utf8.t and t/04.utf8.txt. + +=head2 read_string($string) + +The C method takes as argument the contents of a config file +as a string and returns the C object for it. + +=head2 write($filename, [$encoding]) + +Here, the [] indicate an optional parameter. + +The C method generates the file content for the properties, and +writes it to disk to the filename specified. + +$encoding may be used to indicate the encoding of the file, e.g. 'utf8' or 'encoding(iso-8859-1)'. + +Do not add a prefix to $encoding, such as '>' or '>:'. + +Returns true on success or C on error. + +See t/04.utf8.t and t/04.utf8.txt. + +=head2 write_string() + +Generates the file content for the object and returns it as a string. + +=head1 FAQ + +=head2 What happens if a key is repeated? + +The last value is retained, overwriting any previous values. + +See t/06.repeat.key.t. + +=head2 Why can't I put comments at the ends of lines? + +=over 4 + +=item o The # char is only introduces a comment when it's at the start of a line. + +So a line like: + + key=value # A comment + +Sets key to 'value # A comment', which, presumably, you did not intend. + +This conforms to the syntax discussed in L. + +=item o Comments matching /\s\;\s.+$//g; are ignored. + +This means you can't preserve the suffix using: + + key = Prefix ; Suffix + +Result: key is now 'Prefix'. + +But you can do this: + + key = Prefix;Suffix + +Result: key is now 'Prefix;Suffix'. + +Or this: + + key = Prefix; Suffix + +Result: key is now 'Prefix; Suffix'. + +=back + +See t/07.trailing.comment.t. + +=head2 Why can't I omit the '=' signs? + +E.g.: + + [Things] + my = + list = + of = + things = + +Instead of: + + [Things] + my + list + of + things + +Because the use of '=' signs is a type of mandatory documentation. It indicates that that section +contains 4 items, and not 1 odd item split over 4 lines. + +=head2 Why do I have to assign the result of a method call to a variable? + +This question comes from RT#85386. + +Yes, the syntax may seem odd, but you don't have to call both new() and read_string(). + +Try: + + perl -MData::Dumper -MConfig::Tiny -E 'my $c=Config::Tiny->read_string("one=s"); say Dumper $c' + +Or: + + my($config) = Config::Tiny -> read_string('alpha=bet'); + my($value) = $$config{_}{alpha}; # $value is 'bet'. + +Or even, a bit ridiculously: + + my($value) = ${Config::Tiny -> read_string('alpha=bet')}{_}{alpha}; # $value is 'bet'. + +=head2 Can I use a file called '0' (zero)? + +Yes. See t/05.zero.t (test code) and t/0 (test data). + +=head1 CAVEATS + +Some edge cases in section headers are not supported, and additionally may not +be detected when writing the config file. + +Specifically, section headers with leading whitespace, trailing whitespace, +or newlines anywhere in the section header, will not be written correctly +to the file and may cause file corruption. + +=head1 Repository + +L + +=head1 SUPPORT + +Bugs should be reported via the CPAN bug tracker at + +L + +For other issues, or commercial enhancement or support, contact the author. + +=head1 AUTHOR + +Adam Kennedy Eadamk@cpan.orgE + +Maintanence from V 2.15: Ron Savage L. + +=head1 ACKNOWLEGEMENTS + +Thanks to Sherzod Ruzmetov Esherzodr@cpan.orgE for +L, which inspired this module by being not quite +"simple" enough for me :). + +=head1 SEE ALSO + +See, amongst many: L and L. + +See L (and possibly others) for the preservation of the order of the entries +in the file. + +L. Ini On Drugs. + +L + +L + +L + +L. Config data from Perl itself. + +L + +L + +L + +L. Allows nested data. + +L. Author: RJBS. Uses Moose. Extremely complex. + +L. See next few lines: + +L + +L. 1 Star rating. + +L + +=head1 COPYRIGHT + +Copyright 2002 - 2011 Adam Kennedy. + +This program is free software; you can redistribute +it and/or modify it under the same terms as Perl itself. + +The full text of the license can be found in the +LICENSE file included with this module. + +=cut diff --git a/t/0 b/t/0 new file mode 100644 index 0000000..fea8ae8 --- /dev/null +++ b/t/0 @@ -0,0 +1,2 @@ +[init] +a=b diff --git a/t/00.versions.t b/t/00.versions.t new file mode 100644 index 0000000..ff78465 --- /dev/null +++ b/t/00.versions.t @@ -0,0 +1,40 @@ +#/usr/bin/env perl + +use strict; +use warnings; + +# I tried 'require'-ing modules but that did not work. + +use Config::Tiny; # For the version #. + +use Test::More; + +use File::Spec; +use File::Temp; +use strict; +use utf8; + +# ---------------------- + +pass('All external modules loaded'); + +my(@modules) = qw +/ + File::Spec + File::Temp + strict + utf8 +/; + +diag "Testing Config::Tiny V $Config::Tiny::VERSION"; + +for my $module (@modules) +{ + no strict 'refs'; + + my($ver) = ${$module . '::VERSION'} || 'N/A'; + + diag "Using $module V $ver"; +} + +done_testing; diff --git a/t/00.versions.tx b/t/00.versions.tx new file mode 100644 index 0000000..89b0e66 --- /dev/null +++ b/t/00.versions.tx @@ -0,0 +1,34 @@ +#/usr/bin/env perl + +use strict; +use warnings; + +# I tried 'require'-ing modules but that did not work. + +use <: $module_name :>; # For the version #. + +use Test::More; + +<: $module_list_1 :> + +# ---------------------- + +pass('All external modules loaded'); + +my(@modules) = qw +/ +<: $module_list_2 :> +/; + +diag "Testing <: $module_name :> V $<: $module_name :>::VERSION"; + +for my $module (@modules) +{ + no strict 'refs'; + + my($ver) = ${$module . '::VERSION'} || 'N/A'; + + diag "Using $module V $ver"; +} + +done_testing; diff --git a/t/01.compile.t b/t/01.compile.t new file mode 100644 index 0000000..ff72fcc --- /dev/null +++ b/t/01.compile.t @@ -0,0 +1,13 @@ +#!/usr/bin/perl + +# Compile testing for Config::Tiny + +use strict; +BEGIN { + $| = 1; + $^W = 1; +} + +use Test::More tests => 1; + +use_ok('Config::Tiny'); diff --git a/t/02.main.t b/t/02.main.t new file mode 100644 index 0000000..1329155 --- /dev/null +++ b/t/02.main.t @@ -0,0 +1,177 @@ +#!/usr/bin/perl + +# Main testing script for Config::Tiny + +use strict; +BEGIN { + $| = 1; + $^W = 1; +} + +use Config::Tiny (); + +use File::Spec; +use File::Temp; + +use Test::More tests => 33; + +# Warning: There is another version line, in lib/Config/Tiny.pm. + +our $VERSION = '2.28'; + +# -------------------- + +# Check their perl version +is( $Config::Tiny::VERSION, $VERSION, 'Loaded correct version of Config::Tiny' ); + +# Test trivial creation +my $Trivial = Config::Tiny->new; +ok( $Trivial, 'new() returns true' ); +ok( ref $Trivial, 'new() returns a reference' ); +# Legitimate use of UNIVERSAL::isa +ok( UNIVERSAL::isa( $Trivial, 'HASH' ), 'new() returns a hash reference' ); +isa_ok( $Trivial, 'Config::Tiny' ); +ok( scalar keys %$Trivial == 0, 'new() returns an empty object' ); + +# Try to read in a config +my $Config = Config::Tiny->read(File::Spec -> catfile('t', 'test.conf') ); +ok( $Config, 'read() returns true' ); +ok( ref $Config, 'read() returns a reference' ); +# Legitimate use of UNIVERSAL::isa +ok( UNIVERSAL::isa( $Config, 'HASH' ), 'read() returns a hash reference' ); +isa_ok( $Config, 'Config::Tiny' ); + +# Check the structure of the config +my $expected = { + '_' => { + root => 'something', + }, + section => { + one => 'two', + Foo => 'Bar', + this => 'Your Mother!', + blank => '', + }, + 'Section Two' => { + 'something else' => 'blah', + 'remove' => 'whitespace', + }, +}; +bless $expected, 'Config::Tiny'; +is_deeply( $Config, $expected, 'Config structure matches expected' ); + +# Add some stuff to the trivial config and check write_string() for it +$Trivial->{_} = { + root1 => 'root2', +}; +$Trivial->{section} = { + foo => 'bar', + this => 'that', + blank => '', +}; +$Trivial->{section2} = { + 'this little piggy' => 'went to market' +}; +my $string = <read_string( $string ); +ok( $Read, 'read_string() returns true' ); +is_deeply( $Read, $Trivial, 'read_string() returns expected value' ); + +my $generated = $Trivial->write_string(); +ok( length $generated, 'write_string() returns something' ); +ok( $generated eq $string, 'write_string() returns the correct file contents' ); + +# The EXLOCK option is for BSD-based systems. + +my($temp_dir) = File::Temp -> newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); +my($temp_file) = File::Spec -> catfile($temp_dir, 'write.test.conf'); + +# Try to write a file +my $rv = $Trivial->write($temp_file); +ok( $rv, 'write() returned true' ); +ok( -e $temp_file, 'write() actually created a file' ); + +# Try to read the config back in +$Read = Config::Tiny->read( $temp_file ); +ok( $Read, 'read() of what we wrote returns true' ); +ok( ref $Read, 'read() of what we wrote returns a reference' ); +# Legitimate use of UNIVERSAL::isa +ok( UNIVERSAL::isa( $Read, 'HASH' ), 'read() of what we wrote returns a hash reference' ); +isa_ok( $Read, 'Config::Tiny' ); + +# Check the structure of what we read back in +is_deeply( $Read, $Trivial, 'What we read matches what we wrote out' ); + + +##################################################################### +# Bugs that happened we don't want to happen again + +SCOPE: { + # Reading in an empty file, or a defined but zero length string, should yield + # a valid, but empty, object. + my $Empty = Config::Tiny->read_string(''); + isa_ok( $Empty, 'Config::Tiny' ); + is( scalar(keys %$Empty), 0, 'Config::Tiny object from empty string, is empty' ); +} + +SCOPE: { + # A Section header like [ section ] doesn't end up at ->{' section '}. + # Trim off whitespace from the section header. + my $string = <<'END_CONFIG'; +# The need to trim off whitespace makes a lot more sense +# when you are trying to maximise readability. +[ /path/to/file.txt ] +this=that + +[ section2] +this=that + +[section3 ] +this=that + +END_CONFIG + + my $Trim = Config::Tiny->read_string($string); + isa_ok( $Trim, 'Config::Tiny' ); + ok( exists $Trim->{'/path/to/file.txt'}, 'First section created' ); + is( $Trim->{'/path/to/file.txt'}->{this}, 'that', 'First section created properly' ); + ok( exists $Trim->{section2}, 'Second section created' ); + is( $Trim->{section2}->{this}, 'that', 'Second section created properly' ); + ok( exists $Trim->{section3}, 'Third section created' ); + is( $Trim->{section3}->{this}, 'that', 'Third section created properly' ); +} + + + + + +###################################################################### +# Refuse to write config files with newlines in them + +SCOPE: { + my $newline = Config::Tiny->new; + $newline->{_}->{string} = "foo\nbar"; + local $@; + my $output = undef; + eval { + $output = $newline->write_string; + }; + is( $output, undef, 'write_string() returns undef on newlines' ); + is( + Config::Tiny->errstr, + "Illegal newlines in property '_.string'", + 'errstr() returns expected error', + ); +} diff --git a/t/03.read.string.t b/t/03.read.string.t new file mode 100644 index 0000000..95e0924 --- /dev/null +++ b/t/03.read.string.t @@ -0,0 +1,17 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use Test::More tests => 2; + +# ------------------------ + +my($string) = <<'EOS'; +param1=One +param2=Two +EOS + +my($config) = Config::Tiny -> read_string($string); + +isa_ok($config, 'Config::Tiny', 'read_string() returns an object'); +ok($$config{_}{param1} eq 'One', 'Access to hashref returns correct value'); diff --git a/t/04.utf8.t b/t/04.utf8.t new file mode 100644 index 0000000..fce18dd --- /dev/null +++ b/t/04.utf8.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use File::Spec; +use File::Temp; + +use Test::More tests => 7; + +use utf8; + +# ------------------------ + +my($config) = Config::Tiny -> read('t/04.utf8.txt', 'utf8'); + +ok($$config{utf8_data}{Name} eq 'Δ Lady', 'Hashref after read() returns correct value'); +ok($$config{utf8_data}{Class} eq 'Reichwaldstraße', 'Hashref after read() returns correct value'); +ok($$config{utf8_data}{Type} eq 'Πηληϊάδεω Ἀχιλῆος', 'Hashref after read() returns correct value'); + +# The EXLOCK option is for BSD-based systems. + +my($temp_dir) = File::Temp -> newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); +my($temp_file) = File::Spec -> catfile($temp_dir, 'write.utf8.conf'); +my($string) =< read_string($string); + +ok($conf1, 'read_string() returns true'); + +is_deeply($conf1, {init => {weird_text => 'Reichwaldstraße'} }, 'read_string() returns expected value'); + +$conf1 -> write($temp_file, 'utf8'); + +my($conf2) = Config::Tiny -> read($temp_file, 'encoding(utf8)'); + +is_deeply($conf1, $conf1, 'write() followed by read() works'); +is_deeply($conf2, {init => {weird_text => 'Reichwaldstraße'} }, 'write() + read() returns expected value'); diff --git a/t/04.utf8.txt b/t/04.utf8.txt new file mode 100644 index 0000000..3375794 --- /dev/null +++ b/t/04.utf8.txt @@ -0,0 +1,4 @@ +[utf8_data] +Name = Δ Lady +Class = Reichwaldstraße +Type = Πηληϊάδεω Ἀχιλῆος diff --git a/t/05.zero.t b/t/05.zero.t new file mode 100644 index 0000000..7417cda --- /dev/null +++ b/t/05.zero.t @@ -0,0 +1,38 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use File::Spec; +use File::Temp; + +use Test::More tests => 6; + +use utf8; + +# ------------------------ + +my($conf0) = Config::Tiny -> read('t/0'); + +ok($$conf0{init}{a} eq 'b', 'Hashref after read() returns correct value'); + +# The EXLOCK option is for BSD-based systems. + +my($temp_dir) = File::Temp -> newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); +my($temp_file) = File::Spec -> catfile($temp_dir, '0'); +my($string) =< read_string($string); + +ok($conf1, 'read_string() returns true'); + +is_deeply($conf1, {init => {a => 'b'} }, 'read_string() returns expected value'); + +$conf1 -> write($temp_file); + +my($conf2) = Config::Tiny -> read($temp_file); + +is_deeply($conf1, $conf1, 'write() followed by read() works'); +is_deeply($conf2, {init => {a => 'b'} }, 'write() + read() returns expected value'); +is_deeply($conf0, {init => {a => 'b'} }, 'write() + read() returns expected value'); diff --git a/t/06.repeat.key.t b/t/06.repeat.key.t new file mode 100644 index 0000000..f06c2a4 --- /dev/null +++ b/t/06.repeat.key.t @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use Test::More tests => 3; + +# ------------------------ + +my($string) = <<'EOS'; +key1=One +key2=Infix +key1=Two +EOS + +my($config) = Config::Tiny -> read_string($string); + +isa_ok($config, 'Config::Tiny', 'read_string() returns an object'); +ok($$config{_}{key1} eq 'Two', 'Access to hashref returns correct value'); +ok($$config{_}{key2} eq 'Infix', 'Access to hashref returns correct value'); diff --git a/t/07.trailing.comment.t b/t/07.trailing.comment.t new file mode 100644 index 0000000..6d18985 --- /dev/null +++ b/t/07.trailing.comment.t @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use Test::More tests => 4; + +# ------------------------ + +my($source1) = 'One ; Two'; +my($source2) = 'One;Two'; +my($source3) = 'One; Two'; + +my($string) = < read_string($string); + +isa_ok($config, 'Config::Tiny', 'read_string() returns an object'); +ok($$config{_}{key1} eq 'One', "Source '$source1' read correctly as '$$config{_}{key1}'"); +ok($$config{_}{key2} eq 'One;Two', "Source '$source2' read correctly as '$$config{_}{key2}'"); +ok($$config{_}{key3} eq 'One; Two', "Source '$source3' read correctly as '$$config{_}{key3}'"); diff --git a/t/08.constructor.t b/t/08.constructor.t new file mode 100644 index 0000000..5a1ef5d --- /dev/null +++ b/t/08.constructor.t @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +use Config::Tiny; + +use Test::More tests => 4; + +# ------------------------ + +my($conf1) = Config::Tiny -> new( { _=>{foo=>"bar"} } ); +my($str1) = $conf1->write_string; +is $str1, "foo=bar\n"; + +my($conf2) = Config::Tiny -> new( { _=>{hello=>"world"}, Cool=>{Beans=>"Dude",someval=>123} } ); +my($str2) = $conf2->write_string; +is $str2, <<'EOF'; +hello=world + +[Cool] +Beans=Dude +someval=123 +EOF + +my($conf3) = Config::Tiny -> new( { one => { alpha=>"aaa", beta=>"bbb" }, + two => { abc => 123, def => 456, ghi => 789 } } ); +my($str3) = $conf3->write_string; +is $str3, <<'EOF'; +[one] +alpha=aaa +beta=bbb + +[two] +abc=123 +def=456 +ghi=789 +EOF + +# from synopsis: +my $config = Config::Tiny->new({ + _ => { rootproperty => "Bar" }, + section => { one => "value", Foo => 42 } }); +is $config->write_string, <<'EOF'; +rootproperty=Bar + +[section] +Foo=42 +one=value +EOF + diff --git a/t/test.conf b/t/test.conf new file mode 100644 index 0000000..50b9307 --- /dev/null +++ b/t/test.conf @@ -0,0 +1,11 @@ +root=something + +[section] +one=two +Foo=Bar +this=Your Mother! +blank= + +[Section Two] +something else=blah + remove = whitespace \ No newline at end of file diff --git a/xt/author/pod.t b/xt/author/pod.t new file mode 100644 index 0000000..170cae0 --- /dev/null +++ b/xt/author/pod.t @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +# Test that the syntax of our POD documentation is valid + +use strict; +BEGIN { + $| = 1; + $^W = 1; +} + +my @MODULES = ( + 'Pod::Simple 3.14', + 'Test::Pod 1.44', +); + +# Don't run tests for installs +use Test::More; +unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) { + plan( skip_all => "Author tests not required for installation" ); +} + +# Load the testing modules +foreach my $MODULE ( @MODULES ) { + eval "use $MODULE"; + if ( $@ ) { + $ENV{RELEASE_TESTING} + ? die( "Failed to load required release-testing module $MODULE" ) + : plan( skip_all => "$MODULE not available for testing" ); + } +} + +all_pod_files_ok(); diff --git a/xt/meta.t b/xt/meta.t new file mode 100644 index 0000000..2f8b2c7 --- /dev/null +++ b/xt/meta.t @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +# Test that our META.yml file matches the current specification. + +use strict; +BEGIN { + $| = 1; + $^W = 1; +} + +my $MODULE = 'Test::CPAN::Meta 0.17'; + +# Don't run tests for installs +use Test::More; +unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) { + plan( skip_all => "Author tests not required for installation" ); +} + +# Load the testing module +eval "use $MODULE"; +if ( $@ ) { + $ENV{RELEASE_TESTING} + ? die( "Failed to load required release-testing module $MODULE" ) + : plan( skip_all => "$MODULE not available for testing" ); +} + +meta_yaml_ok(); diff --git a/xt/pmv.t b/xt/pmv.t new file mode 100644 index 0000000..f285be3 --- /dev/null +++ b/xt/pmv.t @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +# Test that our declared minimum Perl version matches our syntax + +use strict; +BEGIN { + $| = 1; + $^W = 1; +} + +my @MODULES = ( + 'Perl::MinimumVersion 1.27', + 'Test::MinimumVersion 0.101080', +); + +# Don't run tests for installs +use Test::More; +unless ( $ENV{AUTOMATED_TESTING} or $ENV{RELEASE_TESTING} ) { + plan( skip_all => "Author tests not required for installation" ); +} + +# Load the testing modules +foreach my $MODULE ( @MODULES ) { + eval "use $MODULE"; + if ( $@ ) { + $ENV{RELEASE_TESTING} + ? die( "Failed to load required release-testing module $MODULE" ) + : plan( skip_all => "$MODULE not available for testing" ); + } +} + +all_minimum_version_from_metayml_ok();