├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── core ├── src │ ├── Configuration.scala │ ├── JProperties.scala │ ├── Reader.scala │ ├── converter │ │ ├── Extra.scala │ │ ├── ListConverter.scala │ │ └── ValueConverter.scala │ ├── io │ │ ├── BlockFormat.scala │ │ ├── FlatFormat.scala │ │ ├── Format.scala │ │ ├── HierarchyUtils.scala │ │ ├── PropertiesFormat.scala │ │ ├── StandardFormat.scala │ │ └── Utils.scala │ └── package.scala ├── test-resources │ ├── test-config-comments.conf │ └── test-config.conf └── test │ ├── ConfigurationSpec.scala │ ├── ConverterHelper.scala │ ├── ExtraConverterSpec.scala │ ├── JPropertiesSpec.scala │ ├── ReaderSpec.scala │ ├── ValueConverterSpec.scala │ └── io │ ├── BlockFormatSpec.scala │ ├── FlatFormatSpec.scala │ ├── IOHelper.scala │ ├── Standard.scala │ └── StandardParserSpec.scala ├── modules └── yaml │ ├── src │ └── YAMLFormat.scala │ └── test │ └── YAMLFormatSpec.scala ├── notes ├── 0.10.0.markdown ├── 0.10.1.markdown ├── 0.10.2.markdown ├── 0.7.0.markdown ├── 0.8.0.markdown ├── 0.9.0.markdown ├── 1.0.0.markdown └── about.markdown ├── org └── release.org └── project ├── Build.scala ├── build.properties └── plugins.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .#* 3 | *# 4 | 5 | *.dat 6 | *.out 7 | 8 | .history 9 | 10 | target/ 11 | project/boot 12 | project/target -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | scala: 3 | - 2.10.3 4 | - 2.11.2 5 | jdk: 6 | - openjdk7 7 | - openjdk6 8 | - oraclejdk8 9 | branches: 10 | except: 11 | - java 12 | - opt 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | Version 1.0.0 - 2013-01-05 5 | -------------------------- 6 | 7 | ### Compatibility 8 | 9 | - Compatible with Scala 2.9.2 and 2.10.0 (thanks to Aki Saarinen). 10 | - **Dropping support for Scala 2.8.2** 11 | 12 | ### New features 13 | 14 | - Allow Configuration to carry its context name (issue #14 and #15) -- Pablo Lalloni. 15 | - Add `Configuration#detachAll` (issue #19) -- Jussi Virtanen. 16 | - Allow to parse "yes/no" into a Boolean (issue #20) -- Yuri Molchan 17 | 18 | Version 0.10.2 - 2012-05-21 19 | --------------------------- 20 | 21 | ### Improved compatibility 22 | 23 | - Compatible with Scala 2.8.1+ 24 | 25 | ### Bug fix 26 | 27 | - Formats can write (and read) empty string values (issue #13). 28 | 29 | 30 | Version 0.10.1 - 2012-05-06 31 | --------------------------- 32 | 33 | ### Bug fix 34 | 35 | - List values are sanitized when added to a configuration (issue #12). 36 | 37 | ### Better exceptions 38 | 39 | - Loading a non-existing file from claspath produces `FileNotFoundException` 40 | (issue #8) -- Martin Konicek 41 | - Include key in `NoSuchElementException` message (issue #11) -- Jussi Virtanen 42 | 43 | Version 0.10.0 - 2012-02-12 44 | --------------------------- 45 | 46 | Configrity v0.10.0 was refactored to allow a modular project layout. 47 | 48 | ### New features 49 | 50 | - YAML module, allowing to use YAML format for loading/saving configurations (issue #2). 51 | 52 | 53 | Version 0.9.0 - 2011-12-08 54 | -------------------------- 55 | 56 | ### New features 57 | 58 | - Configurations can be loaded directly from the classpath using 59 | `Configuration.loadResource`. 60 | 61 | ### Minor feature 62 | 63 | - Better handling of lists when the configuration is built with 64 | key/values (issue #4) 65 | 66 | ### Code quality 67 | 68 | - Test pass on Windows (issue #3) -- Gerolf Seitz 69 | 70 | 71 | Version 0.8.0 - 2011-09-04 72 | -------------------------- 73 | 74 | ### New feature 75 | 76 | - Flat and Block formats accept an include directive. 77 | 78 | ### Minor feature 79 | 80 | - `Configuration#set` formats lists directly to a convertible representation. 81 | - `BlockFormat` ignores empty blocks 82 | 83 | Version 0.7.0 - 2011-08-20 84 | -------------------------- 85 | 86 | ### New features 87 | 88 | - Parser accepts lists 89 | - List value converters 90 | - Extra converters: Color, File, URI, URL 91 | 92 | Version 0.6.1 - 2011-08-18 93 | -------------------------- 94 | 95 | ### Build process 96 | 97 | - Moved to SBT 0.10.x 98 | 99 | 100 | Version 0.6.0 - 2011-07-11 101 | -------------------------- 102 | 103 | First stable version. 104 | 105 | ### Major Change 106 | 107 | - Changed package `configrity` to `org.streum.configrity`. 108 | 109 | Version 0.5.0 - 2011-07-11 110 | -------------------------- 111 | 112 | ### Major Change 113 | 114 | - Adoption of Map semantic. 115 | 116 | ### New features 117 | 118 | - Value converters fo bytes, shots, longs and floats. 119 | - Reader monads 120 | - A configuration can include another one 121 | - Better Configuration factory methods 122 | 123 | ### Bugfix 124 | 125 | - Empty blocks parsing throw an exception in BlockFormat 126 | 127 | Version 0.4.0 - 2011-07-04 128 | -------------------------- 129 | 130 | ### Licensing 131 | 132 | Configrity is now licensed with the LGPL 133 | 134 | ### New features 135 | 136 | - Hierarchical BlockFormat 137 | - Block manipulations (attach and detach) 138 | 139 | ### Code quality 140 | 141 | - Block and Flat formats have common behavior in common Trait 142 | 143 | Version 0.3.0 - 2011-07-02 144 | -------------------------- 145 | 146 | ### New features 147 | 148 | - Interconversions with java.util.Properties 149 | - java.util.Properties Format 150 | 151 | Version 0.2.0 - 2011-07-01 152 | -------------------------- 153 | 154 | ### New features 155 | 156 | - FlatFormat 157 | - From and to string conversion 158 | . Load and save to files and scala.io.Source 159 | - Pretty text formatting 160 | 161 | ### Code quality 162 | 163 | - 100% test coverage with SCCT 164 | 165 | Version 0.1.0 166 | ------------- 167 | 168 | - Most basic features. 169 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | 168 | ======================================================================== 169 | 170 | 171 | GNU GENERAL PUBLIC LICENSE 172 | Version 3, 29 June 2007 173 | 174 | Copyright (C) 2007 Free Software Foundation, Inc. 175 | Everyone is permitted to copy and distribute verbatim copies 176 | of this license document, but changing it is not allowed. 177 | 178 | Preamble 179 | 180 | The GNU General Public License is a free, copyleft license for 181 | software and other kinds of works. 182 | 183 | The licenses for most software and other practical works are designed 184 | to take away your freedom to share and change the works. By contrast, 185 | the GNU General Public License is intended to guarantee your freedom to 186 | share and change all versions of a program--to make sure it remains free 187 | software for all its users. We, the Free Software Foundation, use the 188 | GNU General Public License for most of our software; it applies also to 189 | any other work released this way by its authors. You can apply it to 190 | your programs, too. 191 | 192 | When we speak of free software, we are referring to freedom, not 193 | price. Our General Public Licenses are designed to make sure that you 194 | have the freedom to distribute copies of free software (and charge for 195 | them if you wish), that you receive source code or can get it if you 196 | want it, that you can change the software or use pieces of it in new 197 | free programs, and that you know you can do these things. 198 | 199 | To protect your rights, we need to prevent others from denying you 200 | these rights or asking you to surrender the rights. Therefore, you have 201 | certain responsibilities if you distribute copies of the software, or if 202 | you modify it: responsibilities to respect the freedom of others. 203 | 204 | For example, if you distribute copies of such a program, whether 205 | gratis or for a fee, you must pass on to the recipients the same 206 | freedoms that you received. You must make sure that they, too, receive 207 | or can get the source code. And you must show them these terms so they 208 | know their rights. 209 | 210 | Developers that use the GNU GPL protect your rights with two steps: 211 | (1) assert copyright on the software, and (2) offer you this License 212 | giving you legal permission to copy, distribute and/or modify it. 213 | 214 | For the developers' and authors' protection, the GPL clearly explains 215 | that there is no warranty for this free software. For both users' and 216 | authors' sake, the GPL requires that modified versions be marked as 217 | changed, so that their problems will not be attributed erroneously to 218 | authors of previous versions. 219 | 220 | Some devices are designed to deny users access to install or run 221 | modified versions of the software inside them, although the manufacturer 222 | can do so. This is fundamentally incompatible with the aim of 223 | protecting users' freedom to change the software. The systematic 224 | pattern of such abuse occurs in the area of products for individuals to 225 | use, which is precisely where it is most unacceptable. Therefore, we 226 | have designed this version of the GPL to prohibit the practice for those 227 | products. If such problems arise substantially in other domains, we 228 | stand ready to extend this provision to those domains in future versions 229 | of the GPL, as needed to protect the freedom of users. 230 | 231 | Finally, every program is threatened constantly by software patents. 232 | States should not allow patents to restrict development and use of 233 | software on general-purpose computers, but in those that do, we wish to 234 | avoid the special danger that patents applied to a free program could 235 | make it effectively proprietary. To prevent this, the GPL assures that 236 | patents cannot be used to render the program non-free. 237 | 238 | The precise terms and conditions for copying, distribution and 239 | modification follow. 240 | 241 | TERMS AND CONDITIONS 242 | 243 | 0. Definitions. 244 | 245 | "This License" refers to version 3 of the GNU General Public License. 246 | 247 | "Copyright" also means copyright-like laws that apply to other kinds of 248 | works, such as semiconductor masks. 249 | 250 | "The Program" refers to any copyrightable work licensed under this 251 | License. Each licensee is addressed as "you". "Licensees" and 252 | "recipients" may be individuals or organizations. 253 | 254 | To "modify" a work means to copy from or adapt all or part of the work 255 | in a fashion requiring copyright permission, other than the making of an 256 | exact copy. The resulting work is called a "modified version" of the 257 | earlier work or a work "based on" the earlier work. 258 | 259 | A "covered work" means either the unmodified Program or a work based 260 | on the Program. 261 | 262 | To "propagate" a work means to do anything with it that, without 263 | permission, would make you directly or secondarily liable for 264 | infringement under applicable copyright law, except executing it on a 265 | computer or modifying a private copy. Propagation includes copying, 266 | distribution (with or without modification), making available to the 267 | public, and in some countries other activities as well. 268 | 269 | To "convey" a work means any kind of propagation that enables other 270 | parties to make or receive copies. Mere interaction with a user through 271 | a computer network, with no transfer of a copy, is not conveying. 272 | 273 | An interactive user interface displays "Appropriate Legal Notices" 274 | to the extent that it includes a convenient and prominently visible 275 | feature that (1) displays an appropriate copyright notice, and (2) 276 | tells the user that there is no warranty for the work (except to the 277 | extent that warranties are provided), that licensees may convey the 278 | work under this License, and how to view a copy of this License. If 279 | the interface presents a list of user commands or options, such as a 280 | menu, a prominent item in the list meets this criterion. 281 | 282 | 1. Source Code. 283 | 284 | The "source code" for a work means the preferred form of the work 285 | for making modifications to it. "Object code" means any non-source 286 | form of a work. 287 | 288 | A "Standard Interface" means an interface that either is an official 289 | standard defined by a recognized standards body, or, in the case of 290 | interfaces specified for a particular programming language, one that 291 | is widely used among developers working in that language. 292 | 293 | The "System Libraries" of an executable work include anything, other 294 | than the work as a whole, that (a) is included in the normal form of 295 | packaging a Major Component, but which is not part of that Major 296 | Component, and (b) serves only to enable use of the work with that 297 | Major Component, or to implement a Standard Interface for which an 298 | implementation is available to the public in source code form. A 299 | "Major Component", in this context, means a major essential component 300 | (kernel, window system, and so on) of the specific operating system 301 | (if any) on which the executable work runs, or a compiler used to 302 | produce the work, or an object code interpreter used to run it. 303 | 304 | The "Corresponding Source" for a work in object code form means all 305 | the source code needed to generate, install, and (for an executable 306 | work) run the object code and to modify the work, including scripts to 307 | control those activities. However, it does not include the work's 308 | System Libraries, or general-purpose tools or generally available free 309 | programs which are used unmodified in performing those activities but 310 | which are not part of the work. For example, Corresponding Source 311 | includes interface definition files associated with source files for 312 | the work, and the source code for shared libraries and dynamically 313 | linked subprograms that the work is specifically designed to require, 314 | such as by intimate data communication or control flow between those 315 | subprograms and other parts of the work. 316 | 317 | The Corresponding Source need not include anything that users 318 | can regenerate automatically from other parts of the Corresponding 319 | Source. 320 | 321 | The Corresponding Source for a work in source code form is that 322 | same work. 323 | 324 | 2. Basic Permissions. 325 | 326 | All rights granted under this License are granted for the term of 327 | copyright on the Program, and are irrevocable provided the stated 328 | conditions are met. This License explicitly affirms your unlimited 329 | permission to run the unmodified Program. The output from running a 330 | covered work is covered by this License only if the output, given its 331 | content, constitutes a covered work. This License acknowledges your 332 | rights of fair use or other equivalent, as provided by copyright law. 333 | 334 | You may make, run and propagate covered works that you do not 335 | convey, without conditions so long as your license otherwise remains 336 | in force. You may convey covered works to others for the sole purpose 337 | of having them make modifications exclusively for you, or provide you 338 | with facilities for running those works, provided that you comply with 339 | the terms of this License in conveying all material for which you do 340 | not control copyright. Those thus making or running the covered works 341 | for you must do so exclusively on your behalf, under your direction 342 | and control, on terms that prohibit them from making any copies of 343 | your copyrighted material outside their relationship with you. 344 | 345 | Conveying under any other circumstances is permitted solely under 346 | the conditions stated below. Sublicensing is not allowed; section 10 347 | makes it unnecessary. 348 | 349 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 350 | 351 | No covered work shall be deemed part of an effective technological 352 | measure under any applicable law fulfilling obligations under article 353 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 354 | similar laws prohibiting or restricting circumvention of such 355 | measures. 356 | 357 | When you convey a covered work, you waive any legal power to forbid 358 | circumvention of technological measures to the extent such circumvention 359 | is effected by exercising rights under this License with respect to 360 | the covered work, and you disclaim any intention to limit operation or 361 | modification of the work as a means of enforcing, against the work's 362 | users, your or third parties' legal rights to forbid circumvention of 363 | technological measures. 364 | 365 | 4. Conveying Verbatim Copies. 366 | 367 | You may convey verbatim copies of the Program's source code as you 368 | receive it, in any medium, provided that you conspicuously and 369 | appropriately publish on each copy an appropriate copyright notice; 370 | keep intact all notices stating that this License and any 371 | non-permissive terms added in accord with section 7 apply to the code; 372 | keep intact all notices of the absence of any warranty; and give all 373 | recipients a copy of this License along with the Program. 374 | 375 | You may charge any price or no price for each copy that you convey, 376 | and you may offer support or warranty protection for a fee. 377 | 378 | 5. Conveying Modified Source Versions. 379 | 380 | You may convey a work based on the Program, or the modifications to 381 | produce it from the Program, in the form of source code under the 382 | terms of section 4, provided that you also meet all of these conditions: 383 | 384 | a) The work must carry prominent notices stating that you modified 385 | it, and giving a relevant date. 386 | 387 | b) The work must carry prominent notices stating that it is 388 | released under this License and any conditions added under section 389 | 7. This requirement modifies the requirement in section 4 to 390 | "keep intact all notices". 391 | 392 | c) You must license the entire work, as a whole, under this 393 | License to anyone who comes into possession of a copy. This 394 | License will therefore apply, along with any applicable section 7 395 | additional terms, to the whole of the work, and all its parts, 396 | regardless of how they are packaged. This License gives no 397 | permission to license the work in any other way, but it does not 398 | invalidate such permission if you have separately received it. 399 | 400 | d) If the work has interactive user interfaces, each must display 401 | Appropriate Legal Notices; however, if the Program has interactive 402 | interfaces that do not display Appropriate Legal Notices, your 403 | work need not make them do so. 404 | 405 | A compilation of a covered work with other separate and independent 406 | works, which are not by their nature extensions of the covered work, 407 | and which are not combined with it such as to form a larger program, 408 | in or on a volume of a storage or distribution medium, is called an 409 | "aggregate" if the compilation and its resulting copyright are not 410 | used to limit the access or legal rights of the compilation's users 411 | beyond what the individual works permit. Inclusion of a covered work 412 | in an aggregate does not cause this License to apply to the other 413 | parts of the aggregate. 414 | 415 | 6. Conveying Non-Source Forms. 416 | 417 | You may convey a covered work in object code form under the terms 418 | of sections 4 and 5, provided that you also convey the 419 | machine-readable Corresponding Source under the terms of this License, 420 | in one of these ways: 421 | 422 | a) Convey the object code in, or embodied in, a physical product 423 | (including a physical distribution medium), accompanied by the 424 | Corresponding Source fixed on a durable physical medium 425 | customarily used for software interchange. 426 | 427 | b) Convey the object code in, or embodied in, a physical product 428 | (including a physical distribution medium), accompanied by a 429 | written offer, valid for at least three years and valid for as 430 | long as you offer spare parts or customer support for that product 431 | model, to give anyone who possesses the object code either (1) a 432 | copy of the Corresponding Source for all the software in the 433 | product that is covered by this License, on a durable physical 434 | medium customarily used for software interchange, for a price no 435 | more than your reasonable cost of physically performing this 436 | conveying of source, or (2) access to copy the 437 | Corresponding Source from a network server at no charge. 438 | 439 | c) Convey individual copies of the object code with a copy of the 440 | written offer to provide the Corresponding Source. This 441 | alternative is allowed only occasionally and noncommercially, and 442 | only if you received the object code with such an offer, in accord 443 | with subsection 6b. 444 | 445 | d) Convey the object code by offering access from a designated 446 | place (gratis or for a charge), and offer equivalent access to the 447 | Corresponding Source in the same way through the same place at no 448 | further charge. You need not require recipients to copy the 449 | Corresponding Source along with the object code. If the place to 450 | copy the object code is a network server, the Corresponding Source 451 | may be on a different server (operated by you or a third party) 452 | that supports equivalent copying facilities, provided you maintain 453 | clear directions next to the object code saying where to find the 454 | Corresponding Source. Regardless of what server hosts the 455 | Corresponding Source, you remain obligated to ensure that it is 456 | available for as long as needed to satisfy these requirements. 457 | 458 | e) Convey the object code using peer-to-peer transmission, provided 459 | you inform other peers where the object code and Corresponding 460 | Source of the work are being offered to the general public at no 461 | charge under subsection 6d. 462 | 463 | A separable portion of the object code, whose source code is excluded 464 | from the Corresponding Source as a System Library, need not be 465 | included in conveying the object code work. 466 | 467 | A "User Product" is either (1) a "consumer product", which means any 468 | tangible personal property which is normally used for personal, family, 469 | or household purposes, or (2) anything designed or sold for incorporation 470 | into a dwelling. In determining whether a product is a consumer product, 471 | doubtful cases shall be resolved in favor of coverage. For a particular 472 | product received by a particular user, "normally used" refers to a 473 | typical or common use of that class of product, regardless of the status 474 | of the particular user or of the way in which the particular user 475 | actually uses, or expects or is expected to use, the product. A product 476 | is a consumer product regardless of whether the product has substantial 477 | commercial, industrial or non-consumer uses, unless such uses represent 478 | the only significant mode of use of the product. 479 | 480 | "Installation Information" for a User Product means any methods, 481 | procedures, authorization keys, or other information required to install 482 | and execute modified versions of a covered work in that User Product from 483 | a modified version of its Corresponding Source. The information must 484 | suffice to ensure that the continued functioning of the modified object 485 | code is in no case prevented or interfered with solely because 486 | modification has been made. 487 | 488 | If you convey an object code work under this section in, or with, or 489 | specifically for use in, a User Product, and the conveying occurs as 490 | part of a transaction in which the right of possession and use of the 491 | User Product is transferred to the recipient in perpetuity or for a 492 | fixed term (regardless of how the transaction is characterized), the 493 | Corresponding Source conveyed under this section must be accompanied 494 | by the Installation Information. But this requirement does not apply 495 | if neither you nor any third party retains the ability to install 496 | modified object code on the User Product (for example, the work has 497 | been installed in ROM). 498 | 499 | The requirement to provide Installation Information does not include a 500 | requirement to continue to provide support service, warranty, or updates 501 | for a work that has been modified or installed by the recipient, or for 502 | the User Product in which it has been modified or installed. Access to a 503 | network may be denied when the modification itself materially and 504 | adversely affects the operation of the network or violates the rules and 505 | protocols for communication across the network. 506 | 507 | Corresponding Source conveyed, and Installation Information provided, 508 | in accord with this section must be in a format that is publicly 509 | documented (and with an implementation available to the public in 510 | source code form), and must require no special password or key for 511 | unpacking, reading or copying. 512 | 513 | 7. Additional Terms. 514 | 515 | "Additional permissions" are terms that supplement the terms of this 516 | License by making exceptions from one or more of its conditions. 517 | Additional permissions that are applicable to the entire Program shall 518 | be treated as though they were included in this License, to the extent 519 | that they are valid under applicable law. If additional permissions 520 | apply only to part of the Program, that part may be used separately 521 | under those permissions, but the entire Program remains governed by 522 | this License without regard to the additional permissions. 523 | 524 | When you convey a copy of a covered work, you may at your option 525 | remove any additional permissions from that copy, or from any part of 526 | it. (Additional permissions may be written to require their own 527 | removal in certain cases when you modify the work.) You may place 528 | additional permissions on material, added by you to a covered work, 529 | for which you have or can give appropriate copyright permission. 530 | 531 | Notwithstanding any other provision of this License, for material you 532 | add to a covered work, you may (if authorized by the copyright holders of 533 | that material) supplement the terms of this License with terms: 534 | 535 | a) Disclaiming warranty or limiting liability differently from the 536 | terms of sections 15 and 16 of this License; or 537 | 538 | b) Requiring preservation of specified reasonable legal notices or 539 | author attributions in that material or in the Appropriate Legal 540 | Notices displayed by works containing it; or 541 | 542 | c) Prohibiting misrepresentation of the origin of that material, or 543 | requiring that modified versions of such material be marked in 544 | reasonable ways as different from the original version; or 545 | 546 | d) Limiting the use for publicity purposes of names of licensors or 547 | authors of the material; or 548 | 549 | e) Declining to grant rights under trademark law for use of some 550 | trade names, trademarks, or service marks; or 551 | 552 | f) Requiring indemnification of licensors and authors of that 553 | material by anyone who conveys the material (or modified versions of 554 | it) with contractual assumptions of liability to the recipient, for 555 | any liability that these contractual assumptions directly impose on 556 | those licensors and authors. 557 | 558 | All other non-permissive additional terms are considered "further 559 | restrictions" within the meaning of section 10. If the Program as you 560 | received it, or any part of it, contains a notice stating that it is 561 | governed by this License along with a term that is a further 562 | restriction, you may remove that term. If a license document contains 563 | a further restriction but permits relicensing or conveying under this 564 | License, you may add to a covered work material governed by the terms 565 | of that license document, provided that the further restriction does 566 | not survive such relicensing or conveying. 567 | 568 | If you add terms to a covered work in accord with this section, you 569 | must place, in the relevant source files, a statement of the 570 | additional terms that apply to those files, or a notice indicating 571 | where to find the applicable terms. 572 | 573 | Additional terms, permissive or non-permissive, may be stated in the 574 | form of a separately written license, or stated as exceptions; 575 | the above requirements apply either way. 576 | 577 | 8. Termination. 578 | 579 | You may not propagate or modify a covered work except as expressly 580 | provided under this License. Any attempt otherwise to propagate or 581 | modify it is void, and will automatically terminate your rights under 582 | this License (including any patent licenses granted under the third 583 | paragraph of section 11). 584 | 585 | However, if you cease all violation of this License, then your 586 | license from a particular copyright holder is reinstated (a) 587 | provisionally, unless and until the copyright holder explicitly and 588 | finally terminates your license, and (b) permanently, if the copyright 589 | holder fails to notify you of the violation by some reasonable means 590 | prior to 60 days after the cessation. 591 | 592 | Moreover, your license from a particular copyright holder is 593 | reinstated permanently if the copyright holder notifies you of the 594 | violation by some reasonable means, this is the first time you have 595 | received notice of violation of this License (for any work) from that 596 | copyright holder, and you cure the violation prior to 30 days after 597 | your receipt of the notice. 598 | 599 | Termination of your rights under this section does not terminate the 600 | licenses of parties who have received copies or rights from you under 601 | this License. If your rights have been terminated and not permanently 602 | reinstated, you do not qualify to receive new licenses for the same 603 | material under section 10. 604 | 605 | 9. Acceptance Not Required for Having Copies. 606 | 607 | You are not required to accept this License in order to receive or 608 | run a copy of the Program. Ancillary propagation of a covered work 609 | occurring solely as a consequence of using peer-to-peer transmission 610 | to receive a copy likewise does not require acceptance. However, 611 | nothing other than this License grants you permission to propagate or 612 | modify any covered work. These actions infringe copyright if you do 613 | not accept this License. Therefore, by modifying or propagating a 614 | covered work, you indicate your acceptance of this License to do so. 615 | 616 | 10. Automatic Licensing of Downstream Recipients. 617 | 618 | Each time you convey a covered work, the recipient automatically 619 | receives a license from the original licensors, to run, modify and 620 | propagate that work, subject to this License. You are not responsible 621 | for enforcing compliance by third parties with this License. 622 | 623 | An "entity transaction" is a transaction transferring control of an 624 | organization, or substantially all assets of one, or subdividing an 625 | organization, or merging organizations. If propagation of a covered 626 | work results from an entity transaction, each party to that 627 | transaction who receives a copy of the work also receives whatever 628 | licenses to the work the party's predecessor in interest had or could 629 | give under the previous paragraph, plus a right to possession of the 630 | Corresponding Source of the work from the predecessor in interest, if 631 | the predecessor has it or can get it with reasonable efforts. 632 | 633 | You may not impose any further restrictions on the exercise of the 634 | rights granted or affirmed under this License. For example, you may 635 | not impose a license fee, royalty, or other charge for exercise of 636 | rights granted under this License, and you may not initiate litigation 637 | (including a cross-claim or counterclaim in a lawsuit) alleging that 638 | any patent claim is infringed by making, using, selling, offering for 639 | sale, or importing the Program or any portion of it. 640 | 641 | 11. Patents. 642 | 643 | A "contributor" is a copyright holder who authorizes use under this 644 | License of the Program or a work on which the Program is based. The 645 | work thus licensed is called the contributor's "contributor version". 646 | 647 | A contributor's "essential patent claims" are all patent claims 648 | owned or controlled by the contributor, whether already acquired or 649 | hereafter acquired, that would be infringed by some manner, permitted 650 | by this License, of making, using, or selling its contributor version, 651 | but do not include claims that would be infringed only as a 652 | consequence of further modification of the contributor version. For 653 | purposes of this definition, "control" includes the right to grant 654 | patent sublicenses in a manner consistent with the requirements of 655 | this License. 656 | 657 | Each contributor grants you a non-exclusive, worldwide, royalty-free 658 | patent license under the contributor's essential patent claims, to 659 | make, use, sell, offer for sale, import and otherwise run, modify and 660 | propagate the contents of its contributor version. 661 | 662 | In the following three paragraphs, a "patent license" is any express 663 | agreement or commitment, however denominated, not to enforce a patent 664 | (such as an express permission to practice a patent or covenant not to 665 | sue for patent infringement). To "grant" such a patent license to a 666 | party means to make such an agreement or commitment not to enforce a 667 | patent against the party. 668 | 669 | If you convey a covered work, knowingly relying on a patent license, 670 | and the Corresponding Source of the work is not available for anyone 671 | to copy, free of charge and under the terms of this License, through a 672 | publicly available network server or other readily accessible means, 673 | then you must either (1) cause the Corresponding Source to be so 674 | available, or (2) arrange to deprive yourself of the benefit of the 675 | patent license for this particular work, or (3) arrange, in a manner 676 | consistent with the requirements of this License, to extend the patent 677 | license to downstream recipients. "Knowingly relying" means you have 678 | actual knowledge that, but for the patent license, your conveying the 679 | covered work in a country, or your recipient's use of the covered work 680 | in a country, would infringe one or more identifiable patents in that 681 | country that you have reason to believe are valid. 682 | 683 | If, pursuant to or in connection with a single transaction or 684 | arrangement, you convey, or propagate by procuring conveyance of, a 685 | covered work, and grant a patent license to some of the parties 686 | receiving the covered work authorizing them to use, propagate, modify 687 | or convey a specific copy of the covered work, then the patent license 688 | you grant is automatically extended to all recipients of the covered 689 | work and works based on it. 690 | 691 | A patent license is "discriminatory" if it does not include within 692 | the scope of its coverage, prohibits the exercise of, or is 693 | conditioned on the non-exercise of one or more of the rights that are 694 | specifically granted under this License. You may not convey a covered 695 | work if you are a party to an arrangement with a third party that is 696 | in the business of distributing software, under which you make payment 697 | to the third party based on the extent of your activity of conveying 698 | the work, and under which the third party grants, to any of the 699 | parties who would receive the covered work from you, a discriminatory 700 | patent license (a) in connection with copies of the covered work 701 | conveyed by you (or copies made from those copies), or (b) primarily 702 | for and in connection with specific products or compilations that 703 | contain the covered work, unless you entered into that arrangement, 704 | or that patent license was granted, prior to 28 March 2007. 705 | 706 | Nothing in this License shall be construed as excluding or limiting 707 | any implied license or other defenses to infringement that may 708 | otherwise be available to you under applicable patent law. 709 | 710 | 12. No Surrender of Others' Freedom. 711 | 712 | If conditions are imposed on you (whether by court order, agreement or 713 | otherwise) that contradict the conditions of this License, they do not 714 | excuse you from the conditions of this License. If you cannot convey a 715 | covered work so as to satisfy simultaneously your obligations under this 716 | License and any other pertinent obligations, then as a consequence you may 717 | not convey it at all. For example, if you agree to terms that obligate you 718 | to collect a royalty for further conveying from those to whom you convey 719 | the Program, the only way you could satisfy both those terms and this 720 | License would be to refrain entirely from conveying the Program. 721 | 722 | 13. Use with the GNU Affero General Public License. 723 | 724 | Notwithstanding any other provision of this License, you have 725 | permission to link or combine any covered work with a work licensed 726 | under version 3 of the GNU Affero General Public License into a single 727 | combined work, and to convey the resulting work. The terms of this 728 | License will continue to apply to the part which is the covered work, 729 | but the special requirements of the GNU Affero General Public License, 730 | section 13, concerning interaction through a network will apply to the 731 | combination as such. 732 | 733 | 14. Revised Versions of this License. 734 | 735 | The Free Software Foundation may publish revised and/or new versions of 736 | the GNU General Public License from time to time. Such new versions will 737 | be similar in spirit to the present version, but may differ in detail to 738 | address new problems or concerns. 739 | 740 | Each version is given a distinguishing version number. If the 741 | Program specifies that a certain numbered version of the GNU General 742 | Public License "or any later version" applies to it, you have the 743 | option of following the terms and conditions either of that numbered 744 | version or of any later version published by the Free Software 745 | Foundation. If the Program does not specify a version number of the 746 | GNU General Public License, you may choose any version ever published 747 | by the Free Software Foundation. 748 | 749 | If the Program specifies that a proxy can decide which future 750 | versions of the GNU General Public License can be used, that proxy's 751 | public statement of acceptance of a version permanently authorizes you 752 | to choose that version for the Program. 753 | 754 | Later license versions may give you additional or different 755 | permissions. However, no additional obligations are imposed on any 756 | author or copyright holder as a result of your choosing to follow a 757 | later version. 758 | 759 | 15. Disclaimer of Warranty. 760 | 761 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 762 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 763 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 764 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 765 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 766 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 767 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 768 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 769 | 770 | 16. Limitation of Liability. 771 | 772 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 773 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 774 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 775 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 776 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 777 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 778 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 779 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 780 | SUCH DAMAGES. 781 | 782 | 17. Interpretation of Sections 15 and 16. 783 | 784 | If the disclaimer of warranty and limitation of liability provided 785 | above cannot be given local legal effect according to their terms, 786 | reviewing courts shall apply local law that most closely approximates 787 | an absolute waiver of all civil liability in connection with the 788 | Program, unless a warranty or assumption of liability accompanies a 789 | copy of the Program in return for a fee. 790 | 791 | END OF TERMS AND CONDITIONS 792 | 793 | How to Apply These Terms to Your New Programs 794 | 795 | If you develop a new program, and you want it to be of the greatest 796 | possible use to the public, the best way to achieve this is to make it 797 | free software which everyone can redistribute and change under these terms. 798 | 799 | To do so, attach the following notices to the program. It is safest 800 | to attach them to the start of each source file to most effectively 801 | state the exclusion of warranty; and each file should have at least 802 | the "copyright" line and a pointer to where the full notice is found. 803 | 804 | 805 | Copyright (C) 806 | 807 | This program is free software: you can redistribute it and/or modify 808 | it under the terms of the GNU General Public License as published by 809 | the Free Software Foundation, either version 3 of the License, or 810 | (at your option) any later version. 811 | 812 | This program is distributed in the hope that it will be useful, 813 | but WITHOUT ANY WARRANTY; without even the implied warranty of 814 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 815 | GNU General Public License for more details. 816 | 817 | You should have received a copy of the GNU General Public License 818 | along with this program. If not, see . 819 | 820 | Also add information on how to contact you by electronic and paper mail. 821 | 822 | If the program does terminal interaction, make it output a short 823 | notice like this when it starts in an interactive mode: 824 | 825 | Copyright (C) 826 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 827 | This is free software, and you are welcome to redistribute it 828 | under certain conditions; type `show c' for details. 829 | 830 | The hypothetical commands `show w' and `show c' should show the appropriate 831 | parts of the General Public License. Of course, your program's commands 832 | might be different; for a GUI interface, you would use an "about box". 833 | 834 | You should also get your employer (if you work as a programmer) or school, 835 | if any, to sign a "copyright disclaimer" for the program, if necessary. 836 | For more information on this, and how to apply and follow the GNU GPL, see 837 | . 838 | 839 | The GNU General Public License does not permit incorporating your program 840 | into proprietary programs. If your program is a subroutine library, you 841 | may consider it more useful to permit linking proprietary applications with 842 | the library. If this is what you want to do, use the GNU Lesser General 843 | Public License instead of this License. But first, please read 844 | . 845 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Configrity README file # 2 | 3 | Configrity is a configuration library for Scala 2.10 or 4 | +. Configuration instances are immutable, thread-safe and allow 5 | functional design patterns, such as: 6 | 7 | - Getting options to values 8 | - Converting implicitly values via type-classes 9 | - Reader monad 10 | 11 | The API is stable and covered by tests. 12 | 13 | [![Build Status](https://secure.travis-ci.org/paradigmatic/Configrity.png)](http://travis-ci.org/paradigmatic/Configrity) 14 | 15 | ## Example ## 16 | 17 | import org.streum.configrity._ 18 | 19 | val config = Configuration.load( "server.conf" ) 20 | 21 | val hostname = config[String]("host") 22 | val port = config[Int]("port") 23 | 24 | val updatedConfig = config.set ("port",8080 ) 25 | upddatedConfig.save( "local.conf" ) 26 | 27 | ## Features ## 28 | 29 | ### Implemented ### 30 | 31 | - Automatic values conversions via type classes. 32 | - Loading configurations from any source (File, InputStream, URL, etc.) 33 | - Loading configurations from system properties and environment variables. 34 | - Saving configurations to files. 35 | - Interoperability with java properties. 36 | - Hierarchical configuration blocks. 37 | - Configuration chaining (such as to provide default values). 38 | - Single import: `import org.streum.configrity._` imports all you need. 39 | - Access through reader monads 40 | - Values can be lists (v0.7.0) 41 | - Extra value converters: File, Color, URL, URI (v0.7.0) 42 | - `include` directive is supported, adding compatibility with Akka and 43 | Configgy configurations files. 44 | - Loading configurations from classpath/resources (v0.9.0) 45 | - YAML format support (v0.10.0) 46 | 47 | ### Planned ### 48 | 49 | - More export/Import formats: INI, JSON, XML, apache, etc. 50 | - Build configurations from command line arguments 51 | 52 | ## Requirements ## 53 | 54 | Configrity depends on Scala 2.10 or newer. 55 | 56 | While core features do not depend on external libraries, some modules 57 | may. In that case, the approriate dependencies will be automatically 58 | installed by sbt/maven. 59 | 60 | ## Installation ## 61 | 62 | ### From SBT ### 63 | 64 | The dependency line is: 65 | 66 | "org.streum" %% "configrity-core" % "1.0.0" 67 | 68 | Additional modules require addition dependency lines. Check the wiki 69 | for more information: 70 | 71 | 72 | ### From Maven, Buildr, Ivy, Grape and Grails ### 73 | 74 | Follow the [repository instructions](http://search.maven.org/#artifactdetails|org.streum|configrity-core_2.10|1.0.0|jar). 75 | 76 | 77 | ### From source (master branch) ### 78 | 79 | To install Configrity you just need a working java installation (tested with 80 | JDK 1.6 and 1.7, but may work with 1.5) and SBT **0.13.5**: 81 | 82 | $ git clone git://github.com/paradigmatic/Configrity.git 83 | $ cd Configrity 84 | $ sbt 85 | > test 86 | > doc 87 | > package 88 | 89 | 90 | ## Documentation ## 91 | 92 | See the wiki: 93 | 94 | A scaladoc reference can be found online: 95 | 96 | 97 | ## Collaboration 98 | 99 | ### Reporting bugs and asking for features 100 | 101 | You can github issues tracker, to report bugs or to ask for new features: 102 | 103 | https://github.com/paradigmatic/Configrity/issues 104 | 105 | ### Submitting patches 106 | 107 | Patches are gladly accepted from their original author. Along with any 108 | patches, please state that the patch is your original work and that 109 | you license the work to the Configrity project under the LGPLv3 or 110 | a compatible license. 111 | 112 | To propose a patch, fork the project and send a pull request via 113 | github. Tests are appreciated. 114 | 115 | ## License and ownership ## 116 | 117 | Configrity is a free and open source library released under the 118 | permissive GNU LesserGPLv3 license (see `LICENSE.txt`). 119 | 120 | Copyright (C) 2011-2014. Paradigmatic. All rights reserved. 121 | -------------------------------------------------------------------------------- /core/src/Configuration.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011-2012, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity 21 | 22 | import converter.ValueConverter 23 | import io._ 24 | import scala.io.Source 25 | 26 | /** 27 | * A Configuration class stores and allow access to configuration data. Although 28 | * immutable, several methods allow to easily change configuration data, returning 29 | * a new Configuration instance. 30 | */ 31 | case class Configuration( data: Map[String,String], prefix: Option[String] ) { 32 | 33 | /** 34 | * Constructs a Configuration with the given data and no prefix 35 | */ 36 | def this( data: Map[String,String] ) = this(data, None) 37 | 38 | /** 39 | * Returns true if some value is associated with the 40 | * given key, else false. 41 | */ 42 | def contains( key: String ) = data contains key 43 | 44 | /** 45 | * Retrieve and convert configuration data in the wanted type. Throws 46 | * an NoSuchElementException if the data is not defined. The conversion 47 | * is done by a ValueConverter instance which should be provided or 48 | * implicitly defined elsewhere. 49 | */ 50 | def apply[A]( key: String )( implicit converter: ValueConverter[A] ) = 51 | converter( data get key ) getOrElse ( throw new NoSuchElementException( key ) ) 52 | 53 | /** 54 | * Retrieve and convert configuration data in the wanted type. Returns None 55 | * if the data is not defined and Some(x) else. The conversion is done by 56 | * a ValueConverter instance which should be provided or implicitly defined 57 | * elsewhere. 58 | */ 59 | def get[A]( key: String )( implicit converter: ValueConverter[A] ) = 60 | converter( data get key ) 61 | 62 | /** 63 | * Retrieve and convert configuration data in the wanted type. The user 64 | * must supply a default value, returned is the key is not found. 65 | */ 66 | def apply[A]( key: String, default: A )( implicit converter: ValueConverter[A] ) = 67 | converter( data get key ) getOrElse default 68 | 69 | /** 70 | * Sets a new configuration value. If the key already exists, previous value 71 | * is replaced. 72 | */ 73 | def set[A]( key: String, a: A ) = 74 | Configuration( data + ( key -> a.toString ) ) 75 | 76 | /** 77 | * Sets a new configuration list value. If the key already exists, 78 | * previous value is replaced. 79 | */ 80 | def set[A]( key: String, as: List[A] ) = { 81 | val str = io.Utils.sanitize(as).mkString( "[", ",", "]" ) 82 | Configuration( data + ( key -> str ) ) 83 | } 84 | 85 | /** 86 | * Removes a configuration value. No effect if the value was not previously 87 | * defined. 88 | */ 89 | def clear[A]( key: String ) = 90 | if( data contains key ) 91 | Configuration( data - key ) 92 | else 93 | this 94 | 95 | /** 96 | * Convert the map in a string using a provided export format. 97 | * By default, FlatFormat is used. 98 | */ 99 | def format( fmt: ExportFormat = Configuration.defaultFormat ) = 100 | fmt.toText( this ) 101 | 102 | /** Saves the configuration to a file */ 103 | def save( 104 | file: java.io.File, 105 | fmt: ExportFormat = Configuration.defaultFormat 106 | ) { 107 | val out = new java.io.PrintWriter( file ) 108 | out.println( format(fmt) ) 109 | out.close 110 | } 111 | 112 | /** Saves the configuration to a file */ 113 | def save( filename: String, fmt: ExportFormat ): Unit = 114 | save( new java.io.File( filename ), fmt ) 115 | 116 | /** Saves the configuration to a file */ 117 | def save( filename: String ): Unit = 118 | save( new java.io.File( filename ) ) 119 | 120 | /** Attach a configuration as a sub block. Existing entries with 121 | * same keys will be replaced. Prefix should not end with a 'dot'. 122 | */ 123 | def attach( prefix: String, block: Configuration ) = { 124 | require( 125 | prefix.substring( prefix.length-1) != ".", 126 | "Prefix should not end with a dot" 127 | ) 128 | var nextData = Map[String,String]() 129 | for( (k,v) <- block.data ) { 130 | val nextKey = prefix + "." + k 131 | nextData += nextKey -> v 132 | } 133 | Configuration( data ++ nextData ) 134 | } 135 | 136 | /** Detach all values whose keys have the given prefix as a new configuration. 137 | * The initial configuration is not modified. The prefix is removed in the 138 | * resulting configuration. An important property: 139 | * 140 | *
val c2 = c1.attach(prefix, c1.detach( prefix )
141 | * 142 | * The resulting configuration c2 should be equal to c1. 143 | */ 144 | def detach( prefix: String ) = { 145 | require( 146 | prefix.substring( prefix.length-1) != ".", 147 | "Prefix should not end with a dot" 148 | ) 149 | val regexp = (prefix + """\.(.+)""" ).r 150 | var nextData = Map[String,String]() 151 | for( (k,v) <- data ) k match { 152 | case regexp( subkey ) => nextData += subkey -> v 153 | case _ => {} 154 | } 155 | Configuration( nextData, Some(prefix) ) 156 | } 157 | 158 | /** Detach all values whose keys have a common prefix as a new configuration, 159 | * and repeat this for the entire set of unique prefixes. 160 | * 161 | * The initial configuration is not modified. The prefix is removed in the 162 | * resulting configuration. 163 | */ 164 | def detachAll: Map[String, Configuration] = prefixes.map { prefix => 165 | prefix -> detach(prefix) 166 | }.toMap 167 | 168 | /** Return the set of unique prefixes in this configuration. */ 169 | def prefixes = data.keySet.filter( _.contains('.') ).map( _.split('.').head ) 170 | 171 | /** 172 | * Adds another configuration values providing entries 173 | * which are not present in the present one. Useful 174 | * for defaulting to another Configuration 175 | */ 176 | def include( config: Configuration ) = Configuration( config.data ++ data ) 177 | 178 | /** 179 | * Adds another configuration. The configuration passed in argument 180 | * will override values. 181 | */ 182 | def ++( config: Configuration ) = config include this 183 | 184 | 185 | } 186 | 187 | 188 | /** Configuration companion object */ 189 | 190 | object Configuration { 191 | 192 | /** By default, all conversions are done with BlockFormat */ 193 | val defaultFormat = BlockFormat 194 | 195 | /** Creates a configuration with the given data and no prefix */ 196 | def apply( data: Map[String,String] ): Configuration = 197 | Configuration(data, None) 198 | 199 | /** Creates a configuration from tuples of key,value */ 200 | def apply( entries:(String,Any)* ):Configuration = 201 | Configuration( 202 | entries.map { 203 | case (k,v) => v match { 204 | case l: List[_] => 205 | (k, io.Utils.sanitize(l).mkString("[",",","]") ) 206 | case _ => (k,v.toString) 207 | } 208 | }.toMap 209 | ) 210 | 211 | 212 | /** Returns the environement variables as a Configuration */ 213 | def environment = Configuration( sys.env ) 214 | 215 | /** Returns the system properties as a Configuration */ 216 | def systemProperties = Configuration( sys.props.toMap ) 217 | 218 | /** Instanciates a configuration file from a string using 219 | * eventually a specified format. By default, the FlatFormat 220 | * will be used. 221 | */ 222 | def parse( s: String, fmt: ImportFormat = defaultFormat ) = 223 | fmt.fromText( s ) 224 | 225 | /** 226 | * Load a configuration from a scala.io.Source. An optional 227 | * format can be passed. 228 | */ 229 | def load( source: Source, 230 | fmt: ImportFormat = defaultFormat ): Configuration = 231 | fmt.fromText( source.mkString ) 232 | 233 | /** 234 | * Load a configuration from a file specified by a filename. 235 | */ 236 | def load( fileName: String ) 237 | (implicit codec: scala.io.Codec): Configuration = 238 | load( Source.fromFile( fileName ) ) 239 | 240 | /** 241 | * Load a configuration from a file specified by a filename and 242 | * using a given format. 243 | */ 244 | def load( fileName: String, fmt: ImportFormat ) 245 | (implicit codec: scala.io.Codec): Configuration = 246 | load( Source.fromFile( fileName ), fmt ) 247 | 248 | /** 249 | * Load a configuration as a resource from the classpath. An optional 250 | * format can be passed. 251 | */ 252 | def loadResource( fileName: String, fmt: ImportFormat = defaultFormat ) = { 253 | val inputStream = getClass.getResourceAsStream( fileName ) 254 | if (inputStream == null) { 255 | throw new java.io.FileNotFoundException(fileName) 256 | } 257 | val src = Source.fromInputStream(inputStream) 258 | load( src, fmt ) 259 | } 260 | 261 | 262 | } 263 | -------------------------------------------------------------------------------- /core/src/JProperties.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity 21 | 22 | import java.util.Properties 23 | 24 | /** 25 | * Provides method for converting a Configuration in a java.util.Properties 26 | * instance and the opposite. Both methods are defined as implicit conversions. 27 | */ 28 | object JProperties { 29 | 30 | /** 31 | * java.util.Properties plain text format. 32 | */ 33 | val format = io.PropertiesFormat 34 | 35 | /** 36 | * Converts a java.util.Properties instance into a Configuration. 37 | */ 38 | implicit def propertiesToConfiguration( props: Properties ) = { 39 | var values = Map[String,String]() 40 | val it = props.keySet.iterator 41 | while( it.hasNext ) { 42 | val key = it.next.asInstanceOf[String] 43 | values += key -> props.get(key).asInstanceOf[String] 44 | } 45 | Configuration( values ) 46 | } 47 | 48 | /** 49 | * Converts a Configuration into a java.util.Properties instance. 50 | */ 51 | implicit def configurationToProperties( config: Configuration ) = { 52 | val props = new Properties 53 | for( (k,v) <- config.data ) { 54 | props.put(k,v) 55 | } 56 | props 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/Reader.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity 21 | 22 | import converter.ValueConverter 23 | 24 | /** Monadic Reader */ 25 | trait Reader[A] { 26 | 27 | /** Produce a value from a Configuration */ 28 | def apply( c: Configuration ): A 29 | 30 | def map[B]( f: A => B ) = { 31 | val parent = this 32 | new Reader[B] { 33 | def apply( c: Configuration ) = f( parent(c) ) 34 | } 35 | } 36 | def flatMap[B]( f: A => Reader[B] ) = { 37 | val parent = this 38 | new Reader[B] { 39 | def apply( c: Configuration ) = f( parent(c) )(c) 40 | } 41 | } 42 | 43 | } 44 | 45 | case class ConfigurationReader[A: ValueConverter]( 46 | key: String, 47 | default: Option[A] 48 | ) extends Reader[A] { 49 | def apply( c: Configuration ) = if( default.isDefined ) { 50 | c.get[A](key).getOrElse( default.get ) 51 | } else { 52 | c[A](key) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/converter/Extra.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.converter 21 | 22 | import java.io.File 23 | import java.awt.Color 24 | import java.net.URL 25 | import java.net.URI 26 | 27 | /** 28 | * Extra value converters. 29 | */ 30 | trait Extra { 31 | 32 | /** 33 | * Converts a string to a java.io.File object. 34 | */ 35 | implicit val fileConverter = ValueConverter[File]( new File(_) ) 36 | 37 | /** 38 | * Converts a string to a java.awt.Color object. The string must be an hexadecimal 39 | * representation of the color (for instance #ffa0a0). The initial hash ('#') is 40 | * optional. The hexdigits must be exactly 6 and may be in upper or lower case. 41 | */ 42 | implicit object ColorConverter extends ValueConverter[Color] { 43 | 44 | val HexColor = """#?([a-fA-F0-9]{6})""".r 45 | 46 | def parse( s: String ) = s match { 47 | case HexColor(c) => { 48 | val rgb = Integer.parseInt( c.toLowerCase, 16 ) 49 | new Color( rgb ) 50 | } 51 | } 52 | 53 | } 54 | 55 | /** 56 | * Converts a string to an URI. 57 | */ 58 | implicit val URIConverter = ValueConverter[URI]( new URI(_) ) 59 | 60 | /** 61 | * Converts a string to an URL. 62 | */ 63 | implicit val URLConverter = URIConverter map ( _.toURL ) 64 | 65 | } 66 | 67 | 68 | /* 69 | * Extra value converters. 70 | */ 71 | object Extra extends Extra 72 | -------------------------------------------------------------------------------- /core/src/converter/ListConverter.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.converter 21 | 22 | import scala.util.parsing.combinator._ 23 | 24 | /** 25 | * Parse a list of type A. The string representation must be surrounded 26 | * by square brackets: '[...]'. Elements are separed by comma: 'elem1, elem2'. 27 | * Elements are either a single word without reserved characters, or any characters 28 | * surrounded by double quotes '"'. 29 | */ 30 | class ListConverter[A]( implicit val valueConverter: ValueConverter[A] ) 31 | extends ValueConverter[List[A]] { 32 | 33 | def parse( s: String ) = { 34 | ListConverter.Parser parse s map valueConverter.parse 35 | } 36 | 37 | } 38 | 39 | object ListConverter { 40 | 41 | object Parser extends RegexParsers { 42 | 43 | private def unquote( s: String ) = s.substring( 1, s.size - 1 ) 44 | 45 | override val whiteSpace = """(\s+|#[^\n]*\n)+""".r 46 | 47 | val lineSep = "\n" 48 | def word = """([^=\s\n#\{\}\"\[\],])+""".r 49 | def quoted = """"([^"]*)"""".r /*"*/ ^^ { unquote } 50 | 51 | def item = word | quoted 52 | 53 | def items = repsep( item, "," ) 54 | 55 | def list = "[" ~ items ~ "]" ^^ { 56 | case _ ~ lst ~ _ => lst 57 | } 58 | 59 | def parse( in: String ): List[String] = { 60 | parseAll(list, in) match { 61 | case Success( lst , _ ) => lst 62 | case x: NoSuccess => throw new IllegalArgumentException( x.toString ) 63 | } 64 | } 65 | 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /core/src/converter/ValueConverter.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.converter 21 | 22 | /** 23 | * Parse and convert an Option[String] into an Option[A]. 24 | */ 25 | 26 | trait ValueConverter[A] { self => 27 | /** 28 | * Defines how to parse the string. 29 | */ 30 | def parse( s: String ): A 31 | 32 | /** 33 | * Parse the string if defined or return None. 34 | */ 35 | def apply( s: Option[String] ) = s map parse 36 | 37 | /** 38 | * Creates a new ValueConverter by mapping the result of this converter 39 | * to another one. 40 | */ 41 | def map[B]( f: A => B ) = new ValueConverter[B] { 42 | def parse( s: String ) = f( self parse s ) 43 | } 44 | 45 | 46 | } 47 | 48 | /** 49 | * Ease the creation of a value converter. See the ValueConverter.scala source 50 | * file for examples. 51 | */ 52 | object ValueConverter { 53 | def apply[A]( f: String => A ) = new ValueConverter[A] { 54 | def parse( s: String ) = f(s) 55 | } 56 | } 57 | 58 | /** 59 | * Several predefined value converters for basic types: Int, Double, Boolean, etc. 60 | */ 61 | trait DefaultConverters { 62 | 63 | /** 64 | * Converts a string, to itself... Well just an identity converter. 65 | */ 66 | implicit val stringConverter = ValueConverter[String]( s => s ) 67 | 68 | /** 69 | * Convert strings to bytes. 70 | */ 71 | implicit val byteConverter = ValueConverter[Byte]( 72 | s => java.lang.Byte.parseByte(s) 73 | ) 74 | 75 | /** 76 | * Convert strings to shorts. 77 | */ 78 | implicit val shortConverter = ValueConverter[Short]( 79 | s => java.lang.Short.parseShort(s) 80 | ) 81 | 82 | /** 83 | * Convert strings to ints. 84 | */ 85 | implicit val intConverter = ValueConverter[Int]( 86 | s => java.lang.Integer.parseInt(s) 87 | ) 88 | 89 | /** 90 | * Convert strings to longs. 91 | */ 92 | implicit val longConverter = ValueConverter[Long]( 93 | s => java.lang.Long.parseLong(s) 94 | ) 95 | 96 | /** 97 | * Convert strings to floats. 98 | */ 99 | implicit val floatConverter = ValueConverter[Float]( 100 | s => java.lang.Float.parseFloat(s) 101 | ) 102 | 103 | /** 104 | * Convert strings to doubles. 105 | */ 106 | implicit val doubleConverter = ValueConverter[Double]( 107 | s => java.lang.Double.parseDouble(s) 108 | ) 109 | 110 | /** 111 | * Convert strings to Booleans. The strings values: "T", "true", "yes" and "on" 112 | * will be converted to true and the strings: "F", "false", "no" and "off" will 113 | * be converted to false. 114 | */ 115 | implicit val booleanConverter = BooleanConverter 116 | 117 | object BooleanConverter extends ValueConverter[Boolean] { 118 | val trues = Set("T", "true", "yes", "on") 119 | val falses = Set("F", "false", "no", "off") 120 | def parse( s: String ) = { 121 | if( trues contains s ) true 122 | else if ( falses contains s ) false 123 | else throw new IllegalArgumentException( 124 | s + " could not be converted in to a Boolean" 125 | ) 126 | } 127 | } 128 | 129 | /** 130 | * Converts string to Lists on arbitrary element type A. A value 131 | * converter for element type must be available. 132 | */ 133 | implicit def listConverter[A: ValueConverter]: ValueConverter[List[A]] = 134 | new ListConverter[A] 135 | 136 | } 137 | -------------------------------------------------------------------------------- /core/src/io/BlockFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | import org.streum.configrity.Configuration 23 | import scala.collection.mutable.StringBuilder 24 | import scala.util.parsing.combinator._ 25 | 26 | /** 27 | * Encodes the default block format 28 | */ 29 | object BlockFormat extends StandardFormat with HierarchyUtils { 30 | 31 | def toText( configuration: Configuration ) = writeHierarchy( configuration.data ) 32 | 33 | def writeEntry(k:String,v:String,ind:Int,out:StringBuffer) { 34 | out.append( " " * ind ) 35 | .append( k ).append(" = ").append( 36 | sanitizeEmpty( v ) 37 | ).append("\n") 38 | } 39 | def writeBlockStart( k:String, ind:Int, out:StringBuffer ) = 40 | out.append(" "*ind).append(k).append(" {").append("\n") 41 | 42 | def writeBlockEnd( k:String, ind:Int, out:StringBuffer ) = 43 | out.append(" "*ind).append("}").append("\n") 44 | 45 | def parser = new BlockParser 46 | 47 | class BlockParser extends Parser { 48 | 49 | private var blocks = List[String]() 50 | 51 | private def addPrefix( config: Configuration ) = 52 | blocks match { 53 | case Nil => config 54 | case head :: _ => Configuration().attach( head, config ) 55 | } 56 | 57 | val dot = "." 58 | val openBrace = "{" 59 | val closeBrace = "}" 60 | 61 | def blockStart: Parser[Unit] = key ~ openBrace ^^ { 62 | case k ~ _ => blocks ::= k 63 | } 64 | 65 | def emptyBlock = blockStart <~ closeBrace ^^ { 66 | case _ => { 67 | blocks = blocks.tail.tail 68 | Configuration() 69 | } 70 | } 71 | 72 | def block: Parser[Configuration] = 73 | blockStart ~ ( block | emptyBlock | entry ) ~ content ~ closeBrace ^^ { 74 | case _ ~ single ~ rest ~ _ => { 75 | val newLst = addPrefix( single ++ rest ) 76 | blocks = blocks.tail 77 | newLst 78 | } 79 | } 80 | 81 | def content = rep( includeDirective | block | emptyBlock | entry ) ^^ { reduce } 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /core/src/io/FlatFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | import org.streum.configrity.Configuration 23 | 24 | import scala.collection.mutable.StringBuilder 25 | import scala.util.parsing.combinator._ 26 | 27 | /** 28 | * Encodes a simple flat text format. Composed of: 29 | * 30 | * key1 = value1
31 | * key2 = value2
32 | * ... 33 | * 34 | * where keys and values cannot contain an '=' character. Keys 35 | * cannot contain whitespaces, while values can. 36 | * Whitespaces elsewhere are ignored. 37 | * Comment should start with '#' and are ignored. 38 | */ 39 | object FlatFormat extends StandardFormat { 40 | 41 | val sep = "\n" 42 | 43 | def toText( configuration: Configuration ) = { 44 | val out = new StringBuilder 45 | val data = configuration.data 46 | for( k <- data.keySet.toList.sorted ) { 47 | out.append(k).append(" = ").append( 48 | sanitizeEmpty( data(k) ) 49 | ).append(sep) 50 | } 51 | out.toString 52 | } 53 | 54 | def parser = FlatParser 55 | 56 | /** Parser for FlatFormat */ 57 | object FlatParser extends Parser { 58 | def content = rep( entry ) ^^ { reduce } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /core/src/io/Format.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | import org.streum.configrity.Configuration 23 | 24 | /** 25 | * Format for converting a String into a Configuration 26 | */ 27 | trait ImportFormat { 28 | 29 | /** 30 | * Converts a string into a configuration 31 | */ 32 | def fromText( s: String ): Configuration 33 | 34 | } 35 | 36 | /** 37 | * Format for converting a Configuration into a String. 38 | */ 39 | trait ExportFormat { 40 | 41 | /** 42 | * Converts a configuration into a string. 43 | */ 44 | def toText( configuration: Configuration ): String 45 | 46 | } 47 | 48 | /** 49 | * Format able to convert Configuration to String and 50 | * String to Configuration 51 | */ 52 | trait Format extends ImportFormat with ExportFormat 53 | 54 | -------------------------------------------------------------------------------- /core/src/io/HierarchyUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | /** 23 | * Useful methods for implementing hierarchical output formats. 24 | */ 25 | 26 | trait HierarchyUtils { 27 | 28 | private def splitKey( s: String ) = s.split("""\.""").toList 29 | private def joinKey( ss: List[String] ) = ss.mkString(".") 30 | 31 | def writeEntry( k:String, v:String, ind:Int, out:StringBuffer ): Unit 32 | def writeBlockStart( k:String, ind:Int, out:StringBuffer ): Unit 33 | def writeBlockEnd( k:String, ind:Int, out:StringBuffer ): Unit 34 | 35 | 36 | def writeHierarchy( 37 | map: Map[String,String], 38 | indents: Int = 0 39 | ): String = { 40 | val out = new StringBuffer 41 | var blocks = Map[String,Map[String,String]]() 42 | for( (k,v) <- map ) { 43 | splitKey(k) match { 44 | case el :: Nil => { 45 | writeEntry(k,v,indents,out) 46 | } 47 | case first :: rest => { 48 | val subKey = joinKey(rest) 49 | blocks += first -> 50 | ( blocks.getOrElse( first, Map() ) + ( subKey -> v) ) 51 | } 52 | case _ => 53 | } 54 | } 55 | for( (k,block) <- blocks ) { 56 | writeBlockStart( k, indents, out ) 57 | out.append( writeHierarchy( block, indents + 1 ) ) 58 | writeBlockEnd( k, indents, out ) 59 | } 60 | out.toString 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /core/src/io/PropertiesFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | 23 | import org.streum.configrity.Configuration 24 | import org.streum.configrity.JProperties._ 25 | import java.io.StringWriter 26 | import java.io.StringReader 27 | import java.util.Properties 28 | 29 | /** 30 | * Text format described by java.util.Properties javadoc 31 | */ 32 | object PropertiesFormat extends Format { 33 | 34 | def toText( config: Configuration ) = { 35 | val out = new StringWriter 36 | config.store( out, "") 37 | out.close 38 | out.toString 39 | } 40 | 41 | def fromText( s: String ) = { 42 | val in = new StringReader( s ) 43 | val props = new Properties 44 | props.load( in ) 45 | in.close 46 | props: Configuration 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/src/io/StandardFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | import org.streum.configrity.Configuration 23 | 24 | import scala.collection.mutable.StringBuilder 25 | 26 | import scala.util.parsing.combinator._ 27 | 28 | /** 29 | * Encodes the common behavior of standard formats 30 | */ 31 | trait StandardFormat extends Format { 32 | 33 | def sanitizeEmpty( s: String ) = if ( s.isEmpty ) "\"\"" else s 34 | 35 | def parser: Parser 36 | 37 | def fromText( s: String ) = parser.parse( s ) 38 | 39 | trait Parser extends RegexParsers { 40 | 41 | def reduce( lst: List[Configuration] ) = 42 | lst.foldLeft( Configuration() )( _ ++ _ ) 43 | 44 | def unquote( s: String ) = s.substring( 1, s.size - 1 ) 45 | 46 | def protect( s: String ) = word.findFirstIn(s) match { 47 | case Some(z) if s == z => s 48 | case _ => "\"" + s + "\"" 49 | } 50 | 51 | override val whiteSpace = """(\s+|#[^\n]*\n)+""".r 52 | def key = """([^=\s])+""".r 53 | val lineSep = "\n" 54 | def word = """([^=\s\n#\{\}\"\[\],])+""".r 55 | def quoted = """"([^"]*)"""".r /*"*/ ^^ { unquote } 56 | val equals = "=" 57 | 58 | def includeDirective = "include" ~ quoted ^^ { 59 | case _ ~ filename => Configuration.load( filename ) 60 | } 61 | 62 | def item = word | quoted 63 | 64 | def items = repsep( item, "," ) 65 | def list = "[" ~ items ~ "]" ^^ { 66 | case _ ~ lst ~ _ => lst.map( protect ).mkString("[ ", ", ", " ]") 67 | } 68 | 69 | def value = item | list 70 | 71 | def entry = key ~ equals ~ value ^^ { 72 | case k ~ _ ~ v => Configuration( k -> v ) 73 | } 74 | 75 | def content: Parser[Configuration] 76 | 77 | def parse( in: String ) = { 78 | parseAll(content, in) match { 79 | case Success( config , _ ) => config 80 | case x: NoSuccess => throw StandardFormat.ParserException(x.toString) 81 | } 82 | } 83 | 84 | } 85 | 86 | 87 | } 88 | 89 | object StandardFormat{ 90 | 91 | /** Parser exceptions */ 92 | case class ParserException(s: String) extends Exception(s) 93 | 94 | } 95 | -------------------------------------------------------------------------------- /core/src/io/Utils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.io 21 | 22 | object Utils { 23 | 24 | val Alphanum = """\w+""".r 25 | 26 | def sanitize( in: String ): String = in match { 27 | case Alphanum(out) => out 28 | case out => "\"" + out + "\"" 29 | } 30 | 31 | def sanitize[A]( as: List[A] ): List[String] = 32 | as.map( a => sanitize( a.toString ) ) 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/package.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity 2 | 3 | import converter.{ValueConverter,DefaultConverters} 4 | 5 | /** Package object containing the default converters + default formats */ 6 | object `package` extends DefaultConverters { 7 | 8 | val BlockFormat = io.BlockFormat 9 | 10 | val FlatFormat = io.FlatFormat 11 | 12 | /** Returns a reader which retrieves and converts a configuration 13 | * value. If the Configuration does not contain the value, an 14 | * Exception will be thrown. 15 | */ 16 | def read[A: ValueConverter]( key: String ) = 17 | ConfigurationReader[A](key, None) 18 | 19 | /* Returns a reader which retrieves and converts a configuration 20 | * value. If the Configuration does not contain the value, the 21 | * provided default value will be returned. 22 | */ 23 | def read[A: ValueConverter]( key: String, default: A ) = 24 | ConfigurationReader[A](key, Some(default) ) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /core/test-resources/test-config-comments.conf: -------------------------------------------------------------------------------- 1 | foo = true 2 | bar = 2 3 | baz = "hello world" 4 | # key = 5 | 6 | -------------------------------------------------------------------------------- /core/test-resources/test-config.conf: -------------------------------------------------------------------------------- 1 | foo = true 2 | bar = 2 3 | baz = "hello world" -------------------------------------------------------------------------------- /core/test/ConfigurationSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity.Configuration 6 | import org.streum.configrity.converter.DefaultConverters 7 | import org.streum.configrity.io._ 8 | 9 | 10 | class ConfigurationSpec extends FlatSpec with DefaultConverters with io.IOHelper { 11 | 12 | val data = Map("foo"->"FOO", "bar"->"1234", "baz"->"on" ) 13 | val config = Configuration( data ) 14 | 15 | "A configuration" should "return none if it doesn't contain a key when 'getted'" in { 16 | config.get[Int]("buzz") should be (None) 17 | } 18 | 19 | it can "tell if a key is defined" in { 20 | config.contains("foo") should be (true) 21 | config.contains("bar") should be (true) 22 | config.contains("baz") should be (true) 23 | config.contains("buzz") should be (false) 24 | } 25 | 26 | it should "throw exception if it doesn't contain a key when 'applied'" in { 27 | intercept[java.util.NoSuchElementException] { 28 | config[Int]("buzz") 29 | }.getMessage should be ("buzz") 30 | } 31 | 32 | it should "be able to return converted values" in { 33 | config[String]("foo") should be ("FOO") 34 | config[Int]("bar") should be (1234) 35 | config[Boolean]("baz") should be (true) 36 | } 37 | 38 | it should "return a default value when asked" in { 39 | config[String]("foo", "HELLO") should be ("FOO") 40 | config[Int]("bar", 0) should be (1234) 41 | config[Boolean]("baz", false) should be (true) 42 | config[Int]("buzz", 12) should be (12) 43 | } 44 | 45 | it should "be able to add new key" in { 46 | val c2 = config.set("buzz", 0.5) 47 | c2[Double]("buzz") should be (0.5) 48 | config.get[Double]("buzz") should be (None) 49 | } 50 | 51 | it should "be able to replace existing key" in { 52 | val c2 = config.set("foo", 0.5) 53 | c2[Double]("foo") should be (0.5) 54 | config[String]("foo") should be ("FOO") 55 | } 56 | 57 | it should "be able to remove an existing key" in { 58 | val c2 = config.clear("foo") 59 | config[String]("foo") should be ("FOO") 60 | c2.get[String]("foo") should be (None) 61 | } 62 | 63 | it should "not complain when trying to remove an non-existent key" in { 64 | val c2 = config.clear("buzz") 65 | c2 should be (config) 66 | } 67 | 68 | it should "format lists correctly" in { 69 | val lst = List( 1, 2, 3, 5 ) 70 | val c2 = config.set( "list", lst ) 71 | val lst2 = c2[List[Int]]( "list" ) 72 | lst should be (lst2) 73 | } 74 | 75 | it should "format empty lists correctly" in { 76 | val lst = List[Int]( ) 77 | val c2 = config.set( "list", lst ) 78 | val lst2 = c2[List[Int]]( "list" ) 79 | lst should be (lst2) 80 | } 81 | 82 | it should "format lists with empty spaces in values correctly" in { 83 | val lst = List( "hello world" ) 84 | val c2 = config.set( "list", lst ) 85 | val lst2 = c2[List[String]]( "list" ) 86 | lst should be (lst2) 87 | } 88 | 89 | 90 | it can "be nicely formatted" in { 91 | val out = new ExportFormat { 92 | def toText( c: Configuration ) = "FOOBAR" 93 | } 94 | config.format(out) should be ("FOOBAR") 95 | } 96 | 97 | it can "be saved to a file" in { 98 | val fn1 = "/tmp/configrity_configuration_spec_1.conf" 99 | val fn2 = "/tmp/configrity_configuration_spec_2.conf" 100 | autoFile( fn1 ){ file1 => 101 | autoFile( fn2 ){ file2 => 102 | config.save( file1 ) 103 | config.save( file2, FlatFormat ) 104 | val config2 = Configuration.load( file1.getAbsolutePath ) 105 | val config3 = Configuration.load( file2.getAbsolutePath ) 106 | config2 should be (config) 107 | config3 should be (config) 108 | } 109 | } 110 | } 111 | 112 | "A sub configuration" can "be attached at a given prefix" in { 113 | val data2 = Map( "one" -> "1", "two" -> "2" ) 114 | val config2 = Configuration( data2 ) 115 | val config3 = config attach ("nums", config2) 116 | config3[String]("foo") should be ("FOO") 117 | config3[Int]("nums.one") should be (1) 118 | } 119 | 120 | 121 | it can "will replace values when attached at an existing prefix" in { 122 | val data2 = Map( "one" -> "1", "two" -> "2" ) 123 | val config2 = Configuration( data2 ) 124 | val config3 = config attach ("nums", config2) 125 | val data4 = Map( "one" -> "I", "five" -> "V" ) 126 | val config4 = Configuration( data4 ) 127 | val config5 = config3 attach( "nums", config4 ) 128 | config5[String]("foo") should be ("FOO") 129 | config5[String]("nums.one") should be ("I") 130 | config5[Int]("nums.two") should be (2) 131 | config5[String]("nums.five") should be ("V") 132 | } 133 | 134 | it can "be detach from a configuration" in { 135 | val data2 = Map( "one" -> "1", "two" -> "2" ) 136 | val config2 = Configuration( data2 ) 137 | val config3 = config attach ("nums", config2 ) 138 | val config4 = config3 detach ("nums") 139 | config4.data should be (config2.data) 140 | } 141 | 142 | it can "include another configuration" in { 143 | val config2 = Configuration( "foo" -> "fu", "buzz" -> 122 ) 144 | val config3 = config include config2 145 | config3[String]("foo") should be ("FOO") 146 | config3[Int]("bar") should be (1234) 147 | config3[Int]("buzz") should be (122) 148 | (config2 include config) should not be (config include config2) 149 | } 150 | 151 | it should "carry its original prefix" in { 152 | val sup = Configuration() 153 | val sub1 = Configuration() 154 | val sub2 = Configuration() 155 | val full = sup.attach("foo", sub1.attach("bar", sub2)) 156 | full.detach("foo.bar").prefix should be (Some("foo.bar")) 157 | } 158 | 159 | "Sub configurations" can "be detached from a configuration" in { 160 | val sup = Configuration( "foo" -> "bar" ) 161 | val sub1 = Configuration(Map( "one" -> "1", "two" -> "2" )) 162 | val sub2 = Configuration(Map( "first" -> "", "second" -> "b" )) 163 | val full = sup attach ("nums", sub1) attach ("letters", sub2) 164 | full.detachAll should be (Map( 165 | "nums" -> sub1.copy(prefix = Some("nums")), 166 | "letters" -> sub2.copy(prefix = Some("letters")) 167 | )) 168 | } 169 | } 170 | 171 | class ConfigurationObjectSpec extends FlatSpec with io.IOHelper { 172 | 173 | "A configuration" can "be created from the system properties" in { 174 | val config = Configuration.systemProperties 175 | config.get[String]("line.separator") should be ('defined) 176 | } 177 | 178 | it can "be created from environement variables" in { 179 | val config = Configuration.environment 180 | config.get[String]("HOME") should be ('defined) 181 | } 182 | 183 | it can "be created empty" in { 184 | val config = Configuration() 185 | config.data.size should be (0) 186 | } 187 | 188 | it can "be created with key value pairs" in { 189 | val config = Configuration("foo"->"bar", "bazz"->2) 190 | config[String]("foo") should be ("bar") 191 | config[Int]("bazz") should be (2) 192 | } 193 | 194 | it can "support lists directly with key value pairs" in { 195 | val config = Configuration( 196 | "foo"-> List(10), 197 | "bar"-> ("hello"::"world"::Nil) 198 | ) 199 | config[List[Int]]("foo") should be (List(10)) 200 | config[List[String]]("bar") should be (List("hello","world")) 201 | } 202 | 203 | it can "support lists directly with empty spaces in values" in { 204 | val config = Configuration( 205 | "foo"-> List(10), 206 | "bar"-> ("hello world"::Nil) 207 | ) 208 | config[List[Int]]("foo") should be (List(10)) 209 | config[List[String]]("bar") should be (List("hello world")) 210 | } 211 | 212 | 213 | it can "be created from a string using a given format" in { 214 | val s = 215 | """ 216 | foo = true 217 | bar = 2 218 | baz = "hello world" 219 | """ 220 | val config = Configuration.parse( s, FlatFormat ) 221 | config.get[Boolean]("foo") should be (Some(true)) 222 | config.get[Int]("bar") should be (Some(2)) 223 | config.get[String]("baz") should be (Some("hello world")) 224 | } 225 | 226 | it can "be loaded from a file" in { 227 | val fmt = FlatFormat 228 | val s = 229 | """ 230 | foo = true 231 | bar = 2 232 | baz = "hello world" 233 | """ 234 | autoFile( s ){ file => 235 | val fn = file.getAbsolutePath 236 | val config = Configuration.load(fn,fmt) 237 | config.get[Boolean]("foo") should be (Some(true)) 238 | config.get[Int]("bar") should be (Some(2)) 239 | config.get[String]("baz") should be (Some("hello world")) 240 | val config2 = Configuration.load(fn) 241 | config2 should be (config) 242 | } 243 | } 244 | 245 | it can "be loaded from the classpath" in { 246 | val fmt = FlatFormat 247 | val resName = "/test-config.conf" 248 | val config = Configuration.loadResource( resName, fmt ) 249 | config.get[Boolean]("foo") should be (Some(true)) 250 | config.get[Int]("bar") should be (Some(2)) 251 | config.get[String]("baz") should be (Some("hello world")) 252 | val config2 = Configuration.loadResource( resName ) 253 | config2 should be (config) 254 | } 255 | 256 | it must "throw FileNotFoundException when loading non-existing resource from the classpath" in { 257 | val resName = "/non-existing.conf" 258 | the [java.io.FileNotFoundException] thrownBy { 259 | Configuration.loadResource( resName ) 260 | } should have message (resName) 261 | } 262 | } 263 | 264 | -------------------------------------------------------------------------------- /core/test/ConverterHelper.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.streum.configrity.converter._ 4 | 5 | 6 | object ConverterHelper { 7 | 8 | def convert[A]( opt: Option[String] )(implicit converter: ValueConverter[A] ) = 9 | converter(opt) 10 | 11 | def convert[A]( opt: String )(implicit converter: ValueConverter[A] ) = 12 | converter.parse(opt) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/test/ExtraConverterSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.scalatest.WordSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity.converter._ 6 | import java.io.File 7 | import java.awt.Color 8 | import java.net.URI 9 | import java.net.URL 10 | 11 | class ExtraConverterSpec extends WordSpec with DefaultConverters{ 12 | 13 | import ConverterHelper._ 14 | import Extra._ 15 | 16 | "The file converter" should { 17 | "parse a string to File" in { 18 | convert[File]("/tmp") should be (new File("/tmp")) 19 | convert[File]("") should be (new File("")) 20 | } 21 | } 22 | 23 | "The color converter" should { 24 | "parse a string to Color" in { 25 | convert[Color]("000000") should be (new Color(0)) 26 | } 27 | "ignore preceding hash" in { 28 | convert[Color]("#000000") should be (new Color(0)) 29 | convert[Color]("#ffffff") should be (new Color(255,255,255)) 30 | } 31 | "accept a mix of both case" in { 32 | convert[Color]("#fFFfFF") should be (new Color(255,255,255)) 33 | } 34 | "return a exception when the string has invalid chars" in { 35 | intercept[Exception] { 36 | convert[Color]("#00FFX2") 37 | } 38 | } 39 | "return a exception when the string has not 6 hex digits (except hash)" in { 40 | intercept[Exception] { 41 | convert[Color]("#fab") 42 | } 43 | } 44 | } 45 | 46 | "The URI converter" should { 47 | "parse a string to an URI" in { 48 | convert[URI]("http://www.example.com/hello") should 49 | be (new URI("http://www.example.com/hello")) 50 | convert[URI]("./hello") should 51 | be (new URI("./hello")) 52 | } 53 | } 54 | 55 | "The URL converter" should { 56 | "parse a string to an URI" in { 57 | convert[URL]("http://www.example.com/hello") should 58 | be (new URL("http://www.example.com/hello")) 59 | } 60 | "return a exception when the url is not aboslute" in { 61 | intercept[IllegalArgumentException] { 62 | convert[URL]("./hello") 63 | } 64 | } 65 | "return a exception when the string is empty" in { 66 | intercept[IllegalArgumentException] { 67 | convert[URL]("") 68 | } 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /core/test/JPropertiesSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import org.streum.configrity.JProperties 7 | import org.streum.configrity.io._ 8 | import java.util.Properties 9 | 10 | class JPropertiesSpec extends FlatSpec { 11 | 12 | val data = Map("foo"->"FOO", "bar"->"1234", "baz"->"on" ) 13 | val config = Configuration( data ) 14 | 15 | "A Configuration" can "be converted in java properties" in { 16 | val props: Properties = 17 | JProperties.configurationToProperties( config ) 18 | val config2: Configuration = 19 | JProperties.propertiesToConfiguration( props ) 20 | config2 should be (config) 21 | } 22 | 23 | it can "be saved and read in java properties format" in { 24 | val fmt = JProperties.format 25 | val s = config format fmt 26 | val config2 = Configuration.parse( s, fmt ) 27 | config2 should be (config) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/test/ReaderSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import converter.DefaultConverters 7 | 8 | class ReeaderSpec extends FlatSpec with DefaultConverters{ 9 | 10 | val config = Configuration( "foo"->"FOO", "bar"->"1234", "baz"->"on" ) 11 | 12 | "A configuration reader" can "be obtained out of a key" in { 13 | val bar = read[Int]("bar") 14 | bar(config) should be (1234) 15 | } 16 | 17 | it should "throw an exception if the key does not exists" in { 18 | val buzz = read[Int]("buzz") 19 | intercept[Exception] { 20 | buzz(config) should be (1234) 21 | } 22 | } 23 | 24 | it can "have a default value" in { 25 | val bar = read("bar", -101) 26 | val buzz = read("buzz", 12 ) 27 | bar(config) should be (1234) 28 | buzz(config) should be (12) 29 | } 30 | 31 | def extract[A]( reader: Reader[A] ) = reader(config) 32 | def unit[A](a: A) = new Reader[A] { 33 | def apply( c: Configuration ) = a 34 | } 35 | def twice( i: Int ) = unit( 2*i ) 36 | def mkString( i: Int) = unit( i.toString ) 37 | 38 | "Readers" must "respect monad 1st axiom" in { 39 | extract( unit(12).flatMap( twice ) ) should be ( extract(twice(12)) ) 40 | extract( unit(147).flatMap( unit ) ) should be ( extract(unit(147) ) ) 41 | } 42 | 43 | it must "respect monad 2nd axiom" in { 44 | val m = unit( 41 ) 45 | val readerA = m flatMap twice flatMap mkString 46 | val readerB = m flatMap { x => ( twice(x) flatMap mkString ) } 47 | extract( readerA ) should be (extract(readerB)) 48 | } 49 | 50 | it can "be composed" in { 51 | val fooBarBaz = for( s <- read[String]("foo"); 52 | i <- read[Int]("bar"); 53 | b <- read[Boolean]("baz") 54 | ) yield { if( b ) i.toString else s } 55 | 56 | fooBarBaz( config ) should be ("1234") 57 | } 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /core/test/ValueConverterSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test 2 | 3 | import org.scalatest.WordSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity.converter._ 6 | 7 | class ValueConverterSpec extends WordSpec with DefaultConverters{ 8 | 9 | import ConverterHelper._ 10 | 11 | "The string converter" should { 12 | "parse string without changing it" in { 13 | convert[String](None) should be (None) 14 | convert[String](Option("foo")) should be (Option("foo")) 15 | } 16 | } 17 | 18 | "The byte converter" should { 19 | "parse a string in Byte" in { 20 | convert[Byte](Option("12")) should be (Option(12:Byte)) 21 | } 22 | "return a exception when the string cannot be parsed" in { 23 | intercept[Exception] { 24 | convert[Byte](Option("129")) 25 | } 26 | } 27 | } 28 | 29 | "The short converter" should { 30 | "parse a string in short" in { 31 | convert[Short](Option("1234")) should be (Option(1234:Short)) 32 | } 33 | "return a exception when the string cannot be parsed" in { 34 | intercept[Exception] { 35 | convert[Short](Option("12341234")) 36 | } 37 | } 38 | } 39 | 40 | "The int converter" should { 41 | "parse a string in Int" in { 42 | convert[Int](Option("1234")) should be (Option(1234)) 43 | } 44 | "return a exception when the string cannot be parsed" in { 45 | intercept[Exception] { 46 | convert[Int](Option("12.34")) 47 | } 48 | } 49 | } 50 | 51 | "The long converter" should { 52 | "parse a string in Long" in { 53 | convert[Long](Option("1234")) should be (Option(1234:Long)) 54 | } 55 | "return a exception when the string cannot be parsed" in { 56 | intercept[Exception] { 57 | convert[Long](Option("12.34")) 58 | } 59 | } 60 | } 61 | 62 | "The float converter" should { 63 | "parse a string into a Float" in { 64 | convert[Float](Option("1234")) should be (Option(1234.0f)) 65 | convert[Float](Option("1e-9")) should be (Option(1e-9f)) 66 | convert[Float](Option(".1")) should be (Option(.1f)) 67 | } 68 | "return a exception when the string cannot be parsed" in { 69 | intercept[Exception] { 70 | convert[Float](Option("1ef-9")) 71 | } 72 | } 73 | } 74 | 75 | 76 | "The double converter" should { 77 | "parse a string into a Double" in { 78 | convert[Double](Option("1234")) should be (Option(1234.0)) 79 | convert[Double](Option("1e-9")) should be (Option(1e-9)) 80 | convert[Double](Option(".1")) should be (Option(.1)) 81 | } 82 | "return a exception when the string cannot be parsed" in { 83 | intercept[Exception] { 84 | convert[Double](Option("1ef-9")) 85 | } 86 | } 87 | } 88 | 89 | "The boolean converter" should { 90 | "parse a string with true/false into a Boolean" in { 91 | convert[Boolean](Option("false")) should be (Option(false)) 92 | convert[Boolean](Option("true")) should be (Option(true)) 93 | } 94 | "parse a string with T/F into a Boolean" in { 95 | convert[Boolean](Option("F")) should be (Option(false)) 96 | convert[Boolean](Option("T")) should be (Option(true)) 97 | } 98 | "parse a string with yes/no into a Boolean" in { 99 | convert[Boolean](Option("no")) should be (Option(false)) 100 | convert[Boolean](Option("yes")) should be (Option(true)) 101 | } 102 | "parse a string with on/off into a Boolean" in { 103 | convert[Boolean](Option("off")) should be (Option(false)) 104 | convert[Boolean](Option("on")) should be (Option(true)) 105 | } 106 | "return a exception when the string cannot be parsed" in { 107 | intercept[Exception] { 108 | convert[Boolean](Option("False")) 109 | } 110 | } 111 | } 112 | 113 | "The list converter" can { 114 | "parse a string into an Empty List" in { 115 | convert[List[String]]( 116 | Some( "[]" ) 117 | ) should be (Some( Nil )) 118 | } 119 | "parse a string into a List of String" in { 120 | convert[List[String]]( 121 | Some( "[ hello, world ]" ) 122 | ) should be (Some( List("hello", "world" ) )) 123 | } 124 | "parse a string into a List of booleans" in { 125 | convert[List[Boolean]]( 126 | Some( "[on,off,on,off,off]" ) 127 | ) should be (Some( List(true,false,true,false,false) )) 128 | } 129 | "return a exception when the string cannot be parsed" in { 130 | intercept[Exception] { 131 | convert[List[Boolean]](Option("on,off")) 132 | } 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /core/test/io/BlockFormatSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.io 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import org.streum.configrity.io.BlockFormat 7 | import org.streum.configrity.io.BlockFormat._ 8 | import org.streum.configrity.io.StandardFormat.ParserException 9 | 10 | class BlockFormatSpec extends FlatSpec { 11 | 12 | "The block format" can "write and read an empty Configuration" in { 13 | val config = Configuration( ) 14 | fromText( toText( config ) ) should be (config) 15 | } 16 | 17 | it can "write and read a Configuration" in { 18 | val config = Configuration( 19 | Map("foo"->"FOO", "bar"->"1234", "baz"->"on", "empty"->"") 20 | ) 21 | fromText( toText( config ) ) should be (config) 22 | } 23 | 24 | it can "write and read a Configuration with nested blocks" in { 25 | val config = Configuration( 26 | Map( 27 | "foo.gnats.gnits"->"FOO", 28 | "bar.buzz"->"1234", 29 | "bar.baz"->"on", 30 | "lst" -> """[ on, on, off, off ]""" 31 | ) 32 | ) 33 | fromText( toText( config ) ) should be (config) 34 | } 35 | 36 | } 37 | 38 | class BlockFormatParserSpec extends StandardParserSpec with IOHelper { 39 | 40 | def parse( s: String ) = BlockFormat.parser.parse(s) 41 | lazy val parserName = "BlockFormatParser" 42 | 43 | it can "parse nested blocks" in { 44 | val s = 45 | """ 46 | # Example 47 | foo = true 48 | block { 49 | bar = 2 50 | sub { 51 | buzz = hello 52 | } 53 | baz = x 54 | } 55 | """ 56 | val config = parse( s ) 57 | config[Boolean]("foo") should be (true) 58 | config[Int]("block.bar") should be (2) 59 | config[String]("block.baz") should be ("x") 60 | config[String]("block.sub.buzz") should be ("hello") 61 | } 62 | 63 | it can "parse nested blocks mixed with flat notation" in { 64 | val s = 65 | """ 66 | # Example 67 | foo = true 68 | block { 69 | bar = 2 70 | sub { 71 | buzz = hello 72 | } 73 | sub.blah = true 74 | } 75 | block.baz = x 76 | """ 77 | val config = parse( s ) 78 | config[Boolean]("foo") should be (true) 79 | config[Int]("block.bar") should be (2) 80 | config[String]("block.baz") should be ("x") 81 | config[String]("block.sub.buzz") should be ("hello") 82 | config[Boolean]("block.sub.blah") should be (true) 83 | } 84 | 85 | it must "skip all comment with nested blocks" in { 86 | val s = 87 | """ 88 | # Example 89 | foo = true 90 | block { 91 | bar = 2 # ignore 92 | sub { 93 | #comment 94 | buzz = hello 95 | } 96 | baz = x 97 | } 98 | """ 99 | val config = parse( s ) 100 | config[Boolean]("foo") should be (true) 101 | config[Int]("block.bar") should be (2) 102 | config[String]("block.baz") should be ("x") 103 | config[String]("block.sub.buzz") should be ("hello") 104 | } 105 | 106 | it should "ignore whitespaces" in { 107 | val s = 108 | """ 109 | # Example 110 | foo = true 111 | block { 112 | bar= 2 113 | sub { 114 | buzz = hello 115 | } 116 | baz =x 117 | } 118 | """ 119 | val config = parse( s ) 120 | config[Boolean]("foo") should be (true) 121 | config[Int]("block.bar") should be (2) 122 | config[String]("block.baz") should be ("x") 123 | config[String]("block.sub.buzz") should be ("hello") 124 | } 125 | 126 | it must "choke if no key is provided for blocks" in { 127 | val s = 128 | """ 129 | # Example 130 | foo = true 131 | block { 132 | bar = 2 133 | { 134 | buzz = hello 135 | } 136 | sub.blah = true 137 | } 138 | block.baz = x 139 | """ 140 | intercept[ParserException] { 141 | val config = parse( s ) 142 | } 143 | } 144 | 145 | it must "choke if a block is not closed" in { 146 | val s = 147 | """ 148 | # Example 149 | foo = true 150 | block { 151 | bar = 2 152 | sub { 153 | buzz = hello 154 | sub.blah = true 155 | } 156 | block.baz = x 157 | """ 158 | intercept[ParserException] { 159 | val config = parse( s ) 160 | } 161 | } 162 | 163 | it should "merge blocks with same key" in { 164 | val s = 165 | """ 166 | # Example 167 | foo = true 168 | block { 169 | bar = 2 170 | } 171 | block { 172 | bar = x 173 | } 174 | """ 175 | val config = parse( s ) 176 | config[String]("block.bar") should be ("x") 177 | } 178 | 179 | it must "ignore empty blocks" in { 180 | val s = 181 | """ 182 | # Example 183 | foo = true 184 | block { 185 | bar = 2 186 | sub { 187 | 188 | } 189 | baz = x 190 | sub2 { 191 | sub3 { 192 | hoo = false 193 | sub4 { 194 | } 195 | } 196 | } 197 | } 198 | """ 199 | val config = parse( s ) 200 | config[Boolean]("foo") should be (true) 201 | config[Int]("block.bar") should be (2) 202 | config.get[String]("block.sub") should be (None) 203 | config.get[String]("block") should be (None) 204 | config[String]("block.baz") should be ("x") 205 | config[Boolean]("block.sub2.sub3.hoo") should be (false) 206 | config.get[Boolean]("block.sub2.sub3.sub4") should be (None) 207 | } 208 | 209 | it can "parse include directive" in { 210 | val parentContent = """ 211 | block { 212 | foo = true 213 | bar = 2 214 | } 215 | """ 216 | val childContent = """ 217 | include "%s" 218 | 219 | block { 220 | foo = false 221 | baz = "hello" 222 | } 223 | """ 224 | autoFile( parentContent ) { parent => 225 | autoFile( childContent.format(parent.getAbsolutePath) ) { child => 226 | val config = Configuration.load(child.getAbsolutePath) 227 | config[Boolean]("block.foo") should be (false) 228 | config[Int]("block.bar") should be (2) 229 | config[String]("block.baz") should be ("hello") 230 | } 231 | } 232 | } 233 | 234 | it must "choke if the include points to a non existing file" in { 235 | val childContent = """ 236 | include "/tmp/parent.conf" 237 | 238 | block { 239 | foo = false 240 | baz = "hello" 241 | } 242 | """ 243 | autoFile( childContent ) { child => 244 | intercept[java.io.FileNotFoundException] { 245 | val config = Configuration.load( child.getAbsolutePath ) 246 | } 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /core/test/io/FlatFormatSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.io 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import org.streum.configrity.io.FlatFormat._ 7 | import org.streum.configrity.io.FlatFormat 8 | 9 | 10 | class FlatFormatSpec extends FlatSpec { 11 | 12 | "The flat format" can "write and read an empty Configuration" in { 13 | val config = Configuration() 14 | fromText( toText( config ) ) should be (config) 15 | } 16 | 17 | it can "write and read a Configuration" in { 18 | val config = Configuration( 19 | Map("foo"->"FOO", "bar"->"1234", 20 | "baz"->"on", 21 | "lst" -> """[ on, on, off, off ]""", 22 | "empty" -> "" 23 | ) 24 | ) 25 | fromText( toText( config ) ) should be (config) 26 | } 27 | 28 | } 29 | 30 | class FlatFormatParserSpec extends StandardParserSpec with IOHelper{ 31 | 32 | lazy val parserName = "FlatFormatParser" 33 | def parse( s: String ) = FlatFormat.parser.parse(s) 34 | 35 | it can "parse include directive" in { 36 | val parentContent = """ 37 | foo = true 38 | bar = 2 39 | """ 40 | val childContent = """ 41 | include "%s" 42 | foo = false 43 | baz = "hello" 44 | """ 45 | autoFile( parentContent ) { parent => 46 | autoFile( childContent.format(parent.getAbsolutePath) ) { child => 47 | val config = Configuration.load(child.getAbsolutePath) 48 | config[Boolean]("foo") should be (false) 49 | config[Int]("bar") should be (2) 50 | config[String]("baz") should be ("hello") 51 | } 52 | } 53 | } 54 | 55 | it must "choke if the include points to a non existing file" in { 56 | val childContent = """ 57 | include "/tmp/parent.conf" 58 | foo = false 59 | baz = "hello" 60 | """ 61 | autoFile( childContent ) { child => 62 | intercept[java.io.FileNotFoundException] { 63 | val config = Configuration.load(child.getAbsolutePath) 64 | } 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /core/test/io/IOHelper.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.io 2 | 3 | import java.io.File 4 | import java.io.PrintWriter 5 | 6 | trait IOHelper { 7 | 8 | def autoFile[A]( content: String = "" ) 9 | ( body: File => A ) = { 10 | val f = File.createTempFile("configrity", ".conf") 11 | try { 12 | if( content != "" ) { 13 | val out = new PrintWriter( f ) 14 | out println content 15 | out.close 16 | } 17 | body( f ) 18 | } finally { 19 | f.delete 20 | } 21 | } 22 | 23 | 24 | } 25 | 26 | object IOHelper extends IOHelper 27 | -------------------------------------------------------------------------------- /core/test/io/Standard.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.io 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import org.streum.configrity.io.StandardFormat.ParserException 7 | 8 | class Standard extends FlatSpec { 9 | 10 | "a flat format comment line" must "be ignored" in { 11 | val config = Configuration.loadResource("/test-config-comments.conf") 12 | val comment = config("comment", "") 13 | assert(comment == "") 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/test/io/StandardParserSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.io 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | import org.streum.configrity.io.StandardFormat.ParserException 7 | 8 | trait StandardParserSpec extends FlatSpec { 9 | 10 | def parse( s: String ): Configuration 11 | val parserName: String 12 | 13 | 14 | parserName can "parse empty string" in { 15 | val config = parse( "" ) 16 | config.data should be ('empty) 17 | } 18 | 19 | it can "parse a wellformed entry line" in { 20 | val config = parse( "foo = 2" ) 21 | config[Int]("foo") should be (2) 22 | } 23 | 24 | it should "ignore leading and trailing space" in { 25 | val config = parse( " foo = 2" ) 26 | config[Int]("foo") should be (2) 27 | } 28 | 29 | it should "ignore extra spaces around the equal sign" in { 30 | val config = parse( " foo = 2" ) 31 | config[Int]("foo") should be (2) 32 | } 33 | 34 | it should "tolerate an equal sign without space around" in { 35 | val config = parse( " foo=2 " ) 36 | config[Int]("foo") should be (2) 37 | } 38 | 39 | it can "parse several lines" in { 40 | val s = 41 | """ 42 | foo = true 43 | bar = 2 44 | baz = "hello world" 45 | """ 46 | val config = parse( s ) 47 | config[Boolean]("foo") should be (true) 48 | config[Int]("bar") should be (2) 49 | config[String]("baz") should be ("hello world") 50 | } 51 | 52 | it can "parse several badly spaced lines" in { 53 | val s = 54 | """ 55 | foo =true 56 | bar= 2 57 | baz = "hello world" 58 | """ 59 | val config = parse( s ) 60 | config[Boolean]("foo") should be (true) 61 | config[Int]("bar") should be (2) 62 | config[String]("baz") should be ("hello world") 63 | } 64 | 65 | it must "choke when encoutering an unquoted value with spaces" in { 66 | val s = 67 | """ 68 | foo = true 69 | bar = 2 70 | baz = hello world 71 | """ 72 | intercept[ParserException] { 73 | val config = parse( s ) 74 | } 75 | } 76 | 77 | it must "choke when encountering lines with two equals" in { 78 | val s = 79 | """ 80 | foo = true 81 | bar = 2 82 | baz = x = 2 83 | """ 84 | intercept[ParserException] { 85 | val config = parse( s ) 86 | } 87 | } 88 | 89 | it must "choke when encoutering a line without value" in { 90 | val s = 91 | """ 92 | foo = true 93 | bar = 94 | baz = x 95 | """ 96 | intercept[ParserException] { 97 | val config = parse( s ) 98 | } 99 | } 100 | 101 | it must "choke when encoutering a line without key" in { 102 | val s = 103 | """ 104 | foo = true 105 | = 2 106 | baz = x 107 | """ 108 | intercept[ParserException] { 109 | val config = parse( s ) 110 | } 111 | } 112 | 113 | it must "choke when encoutering a line without equals sign" in { 114 | val s = 115 | """ 116 | foo = true 117 | bar 2 118 | baz = x 119 | """ 120 | intercept[ParserException] { 121 | val config = parse( s ) 122 | } 123 | } 124 | 125 | it must "skip comments starting with a '#'" in { 126 | val s = 127 | """ 128 | # Example 129 | foo = true 130 | #bar = 2 131 | baz = x 132 | """ 133 | val config = parse( s ) 134 | config[Boolean]("foo") should be (true) 135 | config.get[Int]("bar") should be (None) 136 | config[String]("baz") should be ("x") 137 | } 138 | 139 | it must "skip comments inline" in { 140 | val s = 141 | """ 142 | # Example 143 | foo = true 144 | bar = 2 # This should "be" skipped 145 | baz = x 146 | """ 147 | val config = parse( s ) 148 | config[Boolean]("foo") should be (true) 149 | config[Int]("bar") should be (2) 150 | config[String]("baz") should be ("x") 151 | } 152 | 153 | it must "skip commented lines when value is missing" in { 154 | val s = 155 | """ 156 | baz = "hello world" 157 | # key = 158 | # foo = true 159 | # bar = 2 160 | """ 161 | val config = parse( s ) 162 | } 163 | 164 | it must "skip commented lines when key and value are missing" in { 165 | val s = 166 | """ 167 | #= 168 | """ 169 | val config = parse( s ) 170 | } 171 | 172 | it can "accept lists" in { 173 | val s = 174 | """ 175 | # Example 176 | foo = [ true, false ] 177 | bar = [1,2, 3,4 ] 178 | baz = [ "hello", "wo,rld" ] 179 | """ 180 | val config = parse( s ) 181 | config[String]("foo") should be ("[ true, false ]") 182 | config[String]("bar") should be ("[ 1, 2, 3, 4 ]") 183 | config[String]("baz") should be ("[ hello, \"wo,rld\" ]") 184 | } 185 | 186 | it can "accept empty strings" in { 187 | val s = 188 | """ 189 | bar = "bar" 190 | foo = "" 191 | baz = "baz" 192 | """ 193 | val config = parse( s ) 194 | config[String]("foo") should be ("") 195 | config[String]("bar") should be ("bar") 196 | config[String]("baz") should be ("baz") 197 | } 198 | 199 | it can "accept empty lists" in { 200 | val s = 201 | """ 202 | # Example 203 | foo = [ "hello" ] 204 | bar = [] 205 | baz = [ "world" ] 206 | """ 207 | val config = parse( s ) 208 | config[String]("foo") should be ("[ hello ]") 209 | config[String]("bar") should be ("[ ]") 210 | config[String]("baz") should be ("[ world ]") 211 | } 212 | 213 | it can "accept lists defined over several lines" in { 214 | val s = 215 | """ 216 | # Example 217 | foo = [ true, false ] 218 | bar = [ 1, 219 | 2, 220 | 3, 221 | 4 222 | ] 223 | baz = [ "hello", 224 | "wo,rld" 225 | ] 226 | """ 227 | val config = parse( s ) 228 | config[String]("foo") should be ("[ true, false ]") 229 | config[String]("bar") should be ("[ 1, 2, 3, 4 ]") 230 | config[String]("baz") should be ("[ hello, \"wo,rld\" ]") 231 | } 232 | 233 | 234 | 235 | it must "choke if a list is not between square brackets" in { 236 | val s = 237 | """ 238 | # Example 239 | foo = [ true, false ] 240 | bar = [1,2, 3,4 241 | baz = [ "hello", "wo,rld" ] 242 | """ 243 | intercept[ParserException] { 244 | val config = parse( s ) 245 | } 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /modules/yaml/src/YAMLFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011, Paradigmatic 3 | 4 | This file is part of Configrity. 5 | 6 | Configrity is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Configrity is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Configrity. If not, see . 18 | */ 19 | 20 | package org.streum.configrity.yaml 21 | 22 | import org.yaml.snakeyaml.Yaml 23 | import org.streum.configrity._ 24 | import org.streum.configrity.io.{Format, HierarchyUtils} 25 | import java.util.{Map => JMap} 26 | import java.util.{List => JList} 27 | import collection.JavaConversions._ 28 | 29 | class YAMLFormatException(msg:String) extends RuntimeException(msg) 30 | 31 | object YAMLFormat extends Format with HierarchyUtils { 32 | 33 | def fromText( s: String ) ={ 34 | val map = yaml2map( s ) 35 | new Configuration( map ) 36 | } 37 | 38 | def toText( configuration: Configuration ) = writeHierarchy( configuration.data ) 39 | 40 | def writeEntry(k:String,v:String,ind:Int,out:StringBuffer) { 41 | out.append( " " * ind ).append( k ).append( ": " ) 42 | .append( v ).append( "\n" ) 43 | } 44 | 45 | def writeBlockStart( k:String, ind:Int, out:StringBuffer ) = 46 | out.append(" "*ind).append(k).append( ": " ).append("\n") 47 | 48 | def writeBlockEnd( k:String, ind:Int, out:StringBuffer ) { } 49 | 50 | private def yaml2map(s: String): Map[String,String] = { 51 | (new Yaml).loadAll(s).head match { 52 | case jmap: JMap[_,_] => readMap( "", jmap.toMap ) 53 | case other => { 54 | val klass = other.getClass 55 | except( "Top level should be a Map. Received: " + klass ) 56 | } 57 | } 58 | } 59 | 60 | private def readMap( prefix: String, map: Map[_,_] ):Map[String,String] = 61 | map.foldLeft( Map[String,String]() ){ 62 | case (map,(k,v)) => map ++ readValue( prefix + k.toString, v ) 63 | //case (map,(k,v)) => map ++ readValue( prefix + k.toString, v ) 64 | } 65 | 66 | 67 | private def readValue(key: String, value: Any ):Map[String,String] = 68 | value match { 69 | case jl: JList[_] => readList( key, jl ) 70 | case jm: JMap[_,_] => readMap( key+".", jm.toMap ) 71 | case null => Map.empty 72 | case _ => Map( key -> value.toString ) 73 | } 74 | 75 | private def readList( key: String, lst: JList[_] ):Map[String,String] = { 76 | val value = lst.toList.map { 77 | case jlist: JList[_] => except("Lists cannot contain nested Lists.") 78 | case jMap: JMap[_,_] => except("Lists cannot contain nested Maps.") 79 | case s => "\"" + s.toString + "\"" 80 | }.mkString( "[", ",", "]" ) 81 | Map( key -> value ) 82 | } 83 | 84 | private def except( msg: String ) = 85 | throw new YAMLFormatException( msg ) 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /modules/yaml/test/YAMLFormatSpec.scala: -------------------------------------------------------------------------------- 1 | package org.streum.configrity.test.yaml 2 | 3 | import org.scalatest.FlatSpec 4 | import org.scalatest.Matchers._ 5 | import org.streum.configrity._ 6 | 7 | import org.streum.configrity.yaml._ 8 | 9 | 10 | class YAMLFormatSpec extends FlatSpec { 11 | 12 | val FMT = YAMLFormat 13 | 14 | "The yaml format" can "parse a simple YAML file" in { 15 | val yml = 16 | """ 17 | foo: 1 18 | bar: true 19 | baz: hello 20 | """ 21 | val config = FMT.fromText( yml ) 22 | config[Int]("foo") should be (1) 23 | config[Boolean]("bar") should be (true) 24 | config[String]("baz") should be ("hello") 25 | 26 | } 27 | 28 | it should "throw an exception if YAML top level is not a map" in { 29 | val yml = 30 | """ 31 | - hello 32 | - world 33 | - 1 34 | """ 35 | intercept[YAMLFormatException]{ 36 | val config = FMT.fromText( yml ) 37 | } 38 | } 39 | 40 | it can "parse list values" in { 41 | val yml = 42 | """ 43 | foo: 1 44 | bar: 45 | - true 46 | - false 47 | - true 48 | baz: hello 49 | """ 50 | val config = FMT.fromText( yml ) 51 | config[Int]("foo") should be (1) 52 | config[List[Boolean]]("bar") should be ( List(true,false,true) ) 53 | config[String]("baz") should be ("hello") 54 | } 55 | 56 | it can "parse inline list values" in { 57 | val yml = 58 | """ 59 | foo: 1 60 | bar: [true, false, true] 61 | baz: hello 62 | """ 63 | val config = FMT.fromText( yml ) 64 | config[Int]("foo") should be (1) 65 | config[List[Boolean]]("bar") should be ( List(true,false,true) ) 66 | config[String]("baz") should be ("hello") 67 | } 68 | 69 | it should "throw an exception if list elements are maps" in { 70 | val yml = 71 | """ 72 | foo: 1 73 | bar: 74 | - inner: 75 | hello: world 76 | tl: dr 77 | baz: hello 78 | """ 79 | intercept[YAMLFormatException]{ 80 | val config = FMT.fromText( yml ) 81 | } 82 | } 83 | 84 | it should "throw an exception if list elements are lists" in { 85 | val yml = 86 | """ 87 | foo: 1 88 | bar: 89 | - inner: 2 90 | - [1, 3, 5] 91 | baz: hello 92 | """ 93 | intercept[YAMLFormatException]{ 94 | val config = FMT.fromText( yml ) 95 | } 96 | } 97 | 98 | it must "parse nested maps as Config blocks" in { 99 | val yml = 100 | """ 101 | foo: 1 102 | bar: 103 | hello: 12 104 | tl: dr 105 | too: 106 | deep: map 107 | also: works 108 | baz: true 109 | """ 110 | val config = FMT.fromText( yml ) 111 | config[Int]("foo") should be (1) 112 | config[String]("baz") should be ("true") 113 | val inner = config.detach("bar") 114 | inner[Int]("hello") should be (12) 115 | inner[String]("tl") should be ("dr") 116 | val inner2 = inner.detach("too") 117 | inner2[String]("deep") should be ("map") 118 | } 119 | 120 | it must "parse only the first YAML document if several present" in { 121 | val yml = 122 | """ 123 | --- 124 | foo: 1 125 | bar: true 126 | baz: hello 127 | --- 128 | foo: 2 129 | bar: false 130 | baz: greetings 131 | """ 132 | val config = FMT.fromText( yml ) 133 | config[Int]("foo") should be (1) 134 | config[Boolean]("bar") should be (true) 135 | config[String]("baz") should be ("hello") 136 | } 137 | 138 | it can "write a config into YAML" in { 139 | val config = Configuration( 140 | "foo.gnats.gnits" -> "FOO", 141 | "bar.buzz" -> 1234, 142 | "bar.baz" -> true, 143 | "lst" -> List( true, true, false, false ) 144 | ) 145 | val config2 = FMT.fromText( FMT.toText( config ) ) 146 | config[List[Boolean]]("lst") should be (config2[List[Boolean]]("lst")) 147 | config[String]("foo.gnats.gnits") should be (config2[String]("foo.gnats.gnits")) 148 | config[Int]("bar.buzz") should be (config2[Int]("bar.buzz")) 149 | config[Boolean]("bar.baz") should be (config2[Boolean]("bar.baz")) 150 | } 151 | 152 | it must "ignore key with empty value" in { 153 | val yml = 154 | """ 155 | mixer: 156 | 157 | hardware: 158 | device: "/dev/ttyUSB0" 159 | """; 160 | val config = FMT.fromText( yml ) 161 | config[String]( "hardware.device" ) should be ( "/dev/ttyUSB0" ) 162 | } 163 | 164 | 165 | 166 | } 167 | -------------------------------------------------------------------------------- /notes/0.10.0.markdown: -------------------------------------------------------------------------------- 1 | Changes since version 0.9.0: 2 | 3 | - Configrity adopted a modular code layout. Most features are provided by the `configrity-core` module, which doesn't require any external dependencies. The sbt dependency line is now slightly different: check the [README file](https://github.com/paradigmatic/Configrity/blob/master/README.md). 4 | 5 | - YAML import/export is now provided by the `configrity-yaml` module, based on [snakeyaml](http://code.google.com/p/snakeyaml/). Usage and documentation in the [project wiki](https://github.com/paradigmatic/Configrity/wiki/YAML) (issue #2). 6 | 7 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). 8 | -------------------------------------------------------------------------------- /notes/0.10.1.markdown: -------------------------------------------------------------------------------- 1 | Small maintenance release, including: 2 | 3 | - When loading a configuration from the classpath, a `FileNotFoundException` is thrown 4 | (issue #8) -- Martin Konicek 5 | - Looking for a non existing key will throw a `NoSuchElementException` with a message 6 | clearly refering to the missing key (issue #11) -- Jussi Virtanen 7 | - List values are sanitized by adding quotes when needed (issue #12) 8 | - Artefacts for Scala 2.9.2 9 | 10 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). 11 | -------------------------------------------------------------------------------- /notes/0.10.2.markdown: -------------------------------------------------------------------------------- 1 | Small maintenance release, including: 2 | 3 | - Thanks to `sbt-scalashim`, Configrity is now backward compatible 4 | with Scala 2.8.1 and 2.8.2. 5 | - Empty string values are properly formatted (issue #13) 6 | 7 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). -------------------------------------------------------------------------------- /notes/0.7.0.markdown: -------------------------------------------------------------------------------- 1 | New features since version 0.6.1: 2 | 3 | - Implicit converters for `File`, `Color`, `URL` and `URI`. 4 | - List of values are accepted: 5 | 6 | -------------------------------------------------------------------------------- /notes/0.8.0.markdown: -------------------------------------------------------------------------------- 1 | Configrity standard formats accepts an `include` directive. This allow 2 | the parsing of *Akka* configuration files as well as most legacy 3 | *Configgy* files. 4 | 5 | New features since version 0.7.0: 6 | 7 | - Setting a list in a configuration will automatically does not 8 | require anymore proper formatting. 9 | - An `include` directive is supported in both `FlatFormat` and 10 | `BlockFormat`. 11 | - `BlockFormat` parser ignores empty blocks without complaining. 12 | 13 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). 14 | -------------------------------------------------------------------------------- /notes/0.9.0.markdown: -------------------------------------------------------------------------------- 1 | Changes since version 0.8.0: 2 | 3 | - Configurations can be loaded directly from the classpath using 4 | `Configuration.loadResource`. 5 | - Better handling of lists when the configuration is built from 6 | key/values (issue #4) 7 | - All tests pass on Windows (issue #3) -- Gerolf Seitz 8 | 9 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). 10 | -------------------------------------------------------------------------------- /notes/1.0.0.markdown: -------------------------------------------------------------------------------- 1 | After 18 months since first public release, we are proud to annouce the first stable version of Configrity. The version was bumped to 1.0.0 to reflect the project maturity. [Semantic versioning](http://semver.org/) will now be strictly enforced. Changes since version 0.10.2: 2 | 3 | ### Scala compatibility 4 | 5 | - Configrity is now compatible with Scala 2.9.2 and 2.10.0 (thanks to Aki Saarinen). 6 | - **Support for Scala 2.8.2 was dropped.** If you are currently using Configrity with Scala 2.8 and if you cannot upgrade to Scala 2.9, you can fill a request using the [issue tracker](https://github.com/paradigmatic/Configrity/issues/). 7 | 8 | ### New features 9 | 10 | - Detached configurations carry their context prefix. (issue #14 and #15) -- Pablo Lalloni. 11 | - The method `Configuration#detachAll` allows to detach all first level sub-configurations to produce a map that associates each prefix with its sub-configuration (issue #19) -- Jussi Virtanen. 12 | - Booleans can be represented as "yes/no" strings (issue #20) -- Yuri Molchan 13 | 14 | If you wish for extra features, [feel free to ask](https://github.com/paradigmatic/Configrity/issues/). -------------------------------------------------------------------------------- /notes/about.markdown: -------------------------------------------------------------------------------- 1 | [Configrity](https://github.com/paradigmatic/Configrity) is a simple, immutable and flexible Scala API for handling configurations. 2 | -------------------------------------------------------------------------------- /org/release.org: -------------------------------------------------------------------------------- 1 | * Release check-list 2 | 3 | ** Before publication [71%] 4 | 5 | - [X] Clean >> All tests green. 6 | - [X] Check version number in =project/Build.scala= 7 | - [X] Update changelog 8 | - [X] Update README file 9 | - [X] Write the new note for implicitly 10 | - [ ] Commit changes 11 | - [ ] Tag release 12 | 13 | ** Publication [0%] 14 | 15 | - [ ] SBT publish 16 | - [ ] Close the stagging repository on http://oss.sonatype.org 17 | - [ ] Release the repository 18 | 19 | ** After publication [0%] 20 | 21 | - [ ] Wait at least two hours 22 | - [ ] Check it appears in MVN central 23 | - [ ] Update README maven section 24 | - [ ] Push and push --tags 25 | - [ ] Generate the scaladoc 26 | - [ ] Push the scaladoc 27 | - [ ] Update the wiki 28 | - [ ] submit the release to implicitly 29 | - [ ] close remaining issues 30 | 31 | ** When everything is OK 32 | 33 | - [ ] Reset this checklist 34 | -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | import com.typesafe.tools.mima.plugin.MimaPlugin.mimaDefaultSettings 5 | import com.typesafe.tools.mima.plugin.MimaKeys.previousArtifact 6 | 7 | object ConfigrityBuild extends Build { 8 | 9 | lazy val configrity = Project( 10 | id = "configrity", 11 | base = file("."), 12 | settings = rootSettings 13 | ).aggregate(core, yaml) 14 | 15 | 16 | lazy val core = Project( 17 | id = "configrity-core", 18 | base = file("core"), 19 | settings = 20 | standardSettings ++ 21 | publishSettings ++ 22 | mimaDefaultSettings ++ 23 | Seq( 24 | previousArtifact := Some("org.streum" % "configrity-core_2.10" % "1.0.0") 25 | ) 26 | ) 27 | 28 | lazy val yaml = Project( 29 | id = "configrity-yaml", 30 | base = file("modules/yaml"), 31 | dependencies = Seq(core), 32 | settings = standardSettings ++ publishSettings ++ Seq( 33 | libraryDependencies += "org.yaml" % "snakeyaml" % "1.11" 34 | ) 35 | ) 36 | 37 | lazy val minimalSettings = Defaults.defaultSettings ++ Seq( 38 | organization := "org.streum", 39 | version := "1.0.1", 40 | licenses := Seq("GNU LesserGPLv3" -> url("http://www.gnu.org/licenses/lgpl.html")), 41 | homepage := Some(url("https://github.com/paradigmatic/Configrity")), 42 | scalaVersion := "2.11.2", 43 | crossScalaVersions := Seq( "2.10.3", "2.11.2" ) 44 | ) 45 | 46 | lazy val rootSettings = minimalSettings ++ Seq( 47 | publish := { }, 48 | publishLocal := { } 49 | ) 50 | 51 | 52 | lazy val scalatest = "org.scalatest" %% "scalatest" % "2.2.0" % "test" 53 | lazy val parsers = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.0" 54 | 55 | lazy val standardSettings = minimalSettings ++ Seq( 56 | libraryDependencies ++= Seq( 57 | scalatest 58 | ), 59 | scalacOptions ++= Seq( "-deprecation", "-unchecked", "-feature", "-language:implicitConversions" ), 60 | scalaSource in Compile <<= baseDirectory(_ / "src"), 61 | scalaSource in Test <<= baseDirectory(_ / "test"), 62 | resourceDirectory in Test <<= baseDirectory { _ / "test-resources" }, 63 | unmanagedClasspath in Compile += 64 | Attributed.blank(new java.io.File("doesnotexist")), 65 | libraryDependencies := { 66 | CrossVersion.partialVersion(scalaVersion.value) match { 67 | case Some((2, scalaMajor)) if scalaMajor >= 11 => 68 | libraryDependencies.value ++ Seq( 69 | "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.2" 70 | ) 71 | case _ => libraryDependencies.value 72 | } 73 | } 74 | ) 75 | 76 | 77 | lazy val publishSettings = Seq( 78 | publishMavenStyle := true, 79 | publishTo <<= version { (v: String) => 80 | val nexus = "https://oss.sonatype.org/" 81 | if (v.trim.endsWith("SNAPSHOT")) 82 | Some("snapshots" at nexus + "content/repositories/snapshots") 83 | else 84 | Some("releases" at nexus + "service/local/staging/deploy/maven2") 85 | }, 86 | publishArtifact in Test := false, 87 | pomIncludeRepository := { x => false }, 88 | pomExtra := requiredPOMextra 89 | ) 90 | 91 | 92 | lazy val requiredPOMextra = { 93 | 94 | https://github.com/paradigmatic/Configrity 95 | scm:git:git@github.com:paradigmatic/Configrity.git 96 | 97 | 98 | 99 | jlfalcone 100 | Jean-Luc Falcone 101 | http://paradigmatic.streum.org/ 102 | 103 | 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.5 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.6") 2 | --------------------------------------------------------------------------------