hocon-1.3.1/0000755000004100000410000000000013704151031012645 5ustar www-datawww-datahocon-1.3.1/README.md0000644000004100000410000001404313704151031014126 0ustar www-datawww-dataruby-hocon ========== [![Gem Version](https://badge.fury.io/rb/hocon.svg)](https://badge.fury.io/rb/hocon) [![Build Status](https://travis-ci.org/puppetlabs/ruby-hocon.png?branch=master)](https://travis-ci.org/puppetlabs/ruby-hocon) This is a port of the [Typesafe Config](https://github.com/typesafehub/config) library to Ruby. The library provides Ruby support for the [HOCON](https://github.com/typesafehub/config/blob/master/HOCON.md) configuration file format. At present, it supports parsing and modification of existing HOCON/JSON files via the `ConfigFactory` class and the `ConfigValueFactory` class, and rendering parsed config objects back to a String ([see examples below](#basic-usage)). It also supports the parsing and modification of HOCON/JSON files via `ConfigDocumentFactory`. **Note:** While the project is production ready, since not all features in the Typesafe library are supported, you may still run into some issues. If you find a problem, feel free to open a github issue. The implementation is intended to be as close to a line-for-line port as the two languages allow, in hopes of making it fairly easy to port over new changesets from the Java code base over time. Support ======= For best results, if you find an issue with this library, please open an issue on our [Jira issue tracker](https://tickets.puppetlabs.com/browse/HC). Issues filed there tend to be more visible to the current maintainers than issues on the Github issue tracker. Basic Usage =========== ```sh gem install hocon ``` To use the simple API, for reading config values: ```rb require 'hocon' conf = Hocon.load("myapp.conf") puts "Here's a setting: #{conf["foo"]["bar"]["baz"]}" ``` By default, the simple API will determine the configuration file syntax/format based on the filename extension of the file; `.conf` will be interpreted as HOCON, `.json` will be interpreted as strict JSON, and any other extension will cause an error to be raised since the syntax is unknown. If you'd like to use a different file extension, you manually specify the syntax, like this: ```rb require 'hocon' require 'hocon/config_syntax' conf = Hocon.load("myapp.blah", {:syntax => Hocon::ConfigSyntax::HOCON}) ``` Supported values for `:syntax` are: JSON, CONF, and HOCON. (CONF and HOCON are aliases, and both map to the underlying HOCON syntax.) To use the ConfigDocument API, if you need both read/write capability for modifying settings in a config file, or if you want to retain access to things like comments and line numbers: ```rb require 'hocon/parser/config_document_factory' require 'hocon/config_value_factory' # The below 4 variables will all be ConfigDocument instances doc = Hocon::Parser::ConfigDocumentFactory.parse_file("myapp.conf") doc2 = doc.set_value("a.b", "[1, 2, 3, 4, 5]") doc3 = doc.remove_value("a") doc4 = doc.set_config_value("a.b", Hocon::ConfigValueFactory.from_any_ref([1, 2, 3, 4, 5])) doc_has_value = doc.has_value?("a") # returns boolean orig_doc_text = doc.render # returns string ``` Note that a `ConfigDocument` is used primarily for simple configuration manipulation while preserving whitespace and comments. As such, it is not powerful as the regular `Config` API, and will not resolve substitutions. CLI Tool ======== The `hocon` gem comes bundles with a `hocon` command line tool which can be used to get and set values from hocon files ``` Usage: hocon [options] {get,set,unset} PATH [VALUE] Example usages: hocon -i settings.conf -o new_settings.conf set some.nested.value 42 hocon -f settings.conf set some.nested.value 42 cat settings.conf | hocon get some.nested.value Subcommands: get PATH - Returns the value at the given path set PATH VALUE - Sets or adds the given value at the given path unset PATH - Removes the value at the given path Options: -i, --in-file HOCON_FILE HOCON file to read/modify. If omitted, STDIN assumed -o, --out-file HOCON_FILE File to be written to. If omitted, STDOUT assumed -f, --file HOCON_FILE File to read/write to. Equivalent to setting -i/-o to the same file -j, --json Output values from the 'get' subcommand in json format -h, --help Show this message -v, --version Show version ``` CLI Examples -------- ### Basic Usage ``` $ cat settings.conf { foo: bar } $ hocon -i settings.conf get foo bar $ hocon -i settings.conf set foo baz $ cat settings.conf { foo: baz } # Write to a different file $ hocon -i settings.conf -o new_settings.conf set some.nested.value 42 $ cat new_settings.conf { foo: bar some: { nested: { value: 42 } } } # Write back to the same file $ hocon -f settings.conf set some.nested.value 42 $ cat settings.conf { foo: bar some: { nested: { value: 42 } } } ``` ### Complex Values If you give `set` a properly formatted hocon dictionary or array, it will try to accept it ``` $ hocon -i settings.conf set foo "{one: [1, 2, 3], two: {hello: world}}" { foo: {one: [1, 2, 3], two: {hello: world}} } ``` ### Chaining If `--in-file` or `--out-file` aren't specified, STDIN and STDOUT are used for the missing options. Therefore it's possible to chain `hocon` calls ``` $ cat settings.conf { foo: bar } $ cat settings.conf | hocon set foo 42 | hocon set one.two three { foo: 42 one: { two: three } } ``` ### JSON Output Calls to the `get` subcommand will return the data in HOCON format by default, but setting the `-j/--json` flag will cause it to return a valid JSON object ``` $ cat settings.conf foo: { bar: { baz: 42 } } $ hocon -i settings.conf get foo --json { "bar": { "baz": 42 } } ``` Testing ======= ```sh bundle install --path .bundle bundle exec rspec spec ``` Unsupported Features ==================== This supports many of the same things as the Java library, but there are some notable exceptions. Unsupported features include: * Non file includes * Loading resources from the class path or URLs * Properties files * Parsing anything other than files and strings * Duration and size settings * Java system properties hocon-1.3.1/bin/0000755000004100000410000000000013704151031013415 5ustar www-datawww-datahocon-1.3.1/bin/hocon0000755000004100000410000000012713704151031014451 0ustar www-datawww-data#!/usr/bin/env ruby require 'hocon/cli' Hocon::CLI.main(Hocon::CLI.parse_args(ARGV)) hocon-1.3.1/CHANGELOG.md0000644000004100000410000000701513704151031014461 0ustar www-datawww-data## 1.3.1 This is a bugfix release * Fix a bug when using the library in multiple threads ([HC-105](https://tickets.puppetlabs.com/browse/HC-105)) ## 1.3.0 This is a feature release * Support environment variable lists ([HC-104](https://tickets.puppetlabs.com/browse/HC-104)) ## 1.2.6 This is a bugfix release * Do not ship spec folder with gem ([PA-2942](https://tickets.puppetlabs.com/browse/PA-2942)) ## 1.2.5 This is a bugfix release * Fixed loading files with UTF-8 characters in their file paths ## 1.2.4 This is a feature release. * Added a cli tool called `hocon` for reading and manipulating hocon files Note that the version numbers 1.2.0-1.2.3 were not used because of bugs in our release pipeline we were working out ## 1.1.3 This is a bugfix release. * Fixed bug where Hocon.parse would throw a ConfigNotResolved error if you passed it a String that contained values with substitutions. ## 1.1.2 This is a bugfix release. * Fixed bug where Hocon::ConfigFactory.parse_file was not handling files with BOMs on Windows, causing UTF-8 files to not load properly. ## 1.1.1 This is a bugfix release. * Fixed a bug where an undefined method `value_type_name` error was being thrown due to improper calls to the class method. ## 1.1.0 This is a bugfix/feature release * Fixed a bug where unrecognized config file extensions caused `Hocon.load` to return an empty hash instead of an error. * Added an optional `:syntax` key to the `Hocon.load` method to explicitly specify the file format * Renamed internal usage of `name` methods to avoid overriding built in `Object#name` method ## 1.0.1 This is a bugfix release. The API is stable enough and the code is being used in production, so the version is also being bumped to 1.0.0 * Fixed a bug wherein calling "Hocon.load" would not resolve substitutions. * Fixed a circular dependency between the Hocon and Hocon::ConfigFactory namespaces. Using the Hocon::ConfigFactory class now requires you to use a `require 'hocon/config_factory'` instead of `require hocon` * Add support for hashes with keyword keys ## 1.0.0 This version number was burned. ## 0.9.3 This is a bugfix release. * Fixed a bug wherein inserting an array or a hash into a ConfigDocument would cause "# hardcoded value" comments to be generated before every entry in the hash/array. ## 0.9.2 This is a bugfix release * Fixed a bug wherein attempting to insert a complex value (such as an array or a hash) into an empty ConfigDocument would cause an undefined method error. ## 0.9.1 This is a bugfix release. * Fixed a bug wherein ugly configurations were being generated due to the addition of new objects when a setting is set at a path that does not currently exist in the configuration. Previously, these new objects were being added as single-line objects. They will now be added as multi-line objects if the parent object is a multi-line object or is an empty root object. ## 0.9.0 This is a promotion of the 0.1.0 release with one small bug fix: * Fixed bug wherein using the `set_config_value` method with some parsed values would cause a failure due to surrounding whitespace ## 0.1.0 This is a feature release containing a large number of changes and improvements * Added support for concatenation * Added support for substitutions * Added support for file includes. Other types of includes are not supported * Added the new ConfigDocument API that was recently implemented in the upstream Java library * Improved JSON support * Fixed a large number of small bugs related to various pieces of implementation hocon-1.3.1/LICENSE0000644000004100000410000002607513704151031013664 0ustar www-datawww-dataApache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hocon-1.3.1/lib/0000755000004100000410000000000013704151031013413 5ustar www-datawww-datahocon-1.3.1/lib/hocon/0000755000004100000410000000000013704151031014521 5ustar www-datawww-datahocon-1.3.1/lib/hocon/config_value_type.rb0000644000004100000410000000123013704151031020544 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' # # The type of a configuration value (following the JSON type schema). # module Hocon::ConfigValueType OBJECT = 0 LIST = 1 NUMBER = 2 BOOLEAN = 3 NULL = 4 STRING = 5 def self.value_type_name(config_value_type) case config_value_type when OBJECT then "OBJECT" when LIST then "LIST" when NUMBER then "NUMBER" when BOOLEAN then "BOOLEAN" when NULL then "NULL" when STRING then "STRING" else raise Hocon::ConfigError::ConfigBugOrBrokenError, "Unrecognized value type '#{config_value_type}'" end end end hocon-1.3.1/lib/hocon/parser/0000755000004100000410000000000013704151031016015 5ustar www-datawww-datahocon-1.3.1/lib/hocon/parser/config_node.rb0000644000004100000410000000200013704151031020604 0ustar www-datawww-data# encoding: utf-8 require 'hocon/parser' require 'hocon/config_error' # # An immutable node that makes up the ConfigDocument AST, and which can be # used to reproduce part or all of the original text of an input. # #

# Because this object is immutable, it is safe to use from multiple threads and # there's no need for "defensive copies." # #

# Do not implement interface {@code ConfigNode}; it should only be # implemented by the config library. Arbitrary implementations will not work # because the library internals assume a specific concrete implementation. # Also, this interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::Parser::ConfigNode # # The original text of the input which was used to form this particular node. # @return the original text used to form this node as a String # def render raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigNode should override `render` (#{self.class})" end end hocon-1.3.1/lib/hocon/parser/config_document_factory.rb0000644000004100000410000000176713704151031023247 0ustar www-datawww-data# encoding: utf-8 require 'hocon/parser' require 'hocon/impl/parseable' require 'hocon/config_parse_options' # # Factory for creating {@link # com.typesafe.config.parser.ConfigDocument} instances. # class Hocon::Parser::ConfigDocumentFactory # # Parses a file into a ConfigDocument instance. # # @param file # the file to parse # @param options # parse options to control how the file is interpreted # @return the parsed configuration # @throws com.typesafe.config.ConfigException on IO or parse errors # def self.parse_file(file, options = Hocon::ConfigParseOptions.defaults) Hocon::Impl::Parseable.new_file(file, options).parse_config_document end # # Parses a string which should be valid HOCON or JSON. # # @param s string to parse # @param options parse options # @return the parsed configuration # def self.parse_string(s, options = Hocon::ConfigParseOptions.defaults) Hocon::Impl::Parseable.new_string(s, options).parse_config_document end endhocon-1.3.1/lib/hocon/parser/config_document.rb0000644000004100000410000001021613704151031021505 0ustar www-datawww-data# encoding: utf-8 require 'hocon/parser' require 'hocon/config_error' # # Represents an individual HOCON or JSON file, preserving all # formatting and syntax details. This can be used to replace # individual values and exactly render the original text of the # input. # #

# Because this object is immutable, it is safe to use from multiple threads and # there's no need for "defensive copies." # #

# Do not implement interface {@code ConfigDocument}; it should only be # implemented by the config library. Arbitrary implementations will not work # because the library internals assume a specific concrete implementation.# # Also, this interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::Parser::ConfigDocument # # Returns a new ConfigDocument that is a copy of the current ConfigDocument, # but with the desired value set at the desired path. If the path exists, it will # remove all duplicates before the final occurrence of the path, and replace the value # at the final occurrence of the path. If the path does not exist, it will be added. If # the document has an array as the root value, an exception will be thrown. # # @param path the path at which to set the desired value # @param newValue the value to set at the desired path, represented as a string. This # string will be parsed into a ConfigNode using the same options used to # parse the entire document, and the text will be inserted # as-is into the document. Leading and trailing comments, whitespace, or # newlines are not allowed, and if present an exception will be thrown. # If a concatenation is passed in for newValue but the document was parsed # with JSON, the first value in the concatenation will be parsed and inserted # into the ConfigDocument. # @return a copy of the ConfigDocument with the desired value at the desired path # def set_value(path, new_value) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})" end # # Returns a new ConfigDocument that is a copy of the current ConfigDocument, # but with the desired value set at the desired path as with {@link #setValue(String, String)}, # but takes a ConfigValue instead of a string. # # @param path the path at which to set the desired value # @param newValue the value to set at the desired path, represented as a ConfigValue. # The rendered text of the ConfigValue will be inserted into the # ConfigDocument. # @return a copy of the ConfigDocument with the desired value at the desired path # def set_config_value(path, new_value) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})" end # # Returns a new ConfigDocument that is a copy of the current ConfigDocument, but with # the value at the desired path removed. If the desired path does not exist in the document, # a copy of the current document will be returned. If there is an array at the root, an exception # will be thrown. # # @param path the path to remove from the document # @return a copy of the ConfigDocument with the desired value removed from the document. # def remove_value(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})" end # # Returns a boolean indicating whether or not a ConfigDocument has a value at the desired path. # @param path the path to check # @return true if the path exists in the document, otherwise false # def has_value?(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})" end # # The original text of the input, modified if necessary with # any replaced or added values. # @return the modified original text # def render raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})" end endhocon-1.3.1/lib/hocon/impl/0000755000004100000410000000000013704151031015462 5ustar www-datawww-datahocon-1.3.1/lib/hocon/impl/path_builder.rb0000644000004100000410000000176213704151031020457 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/path' require 'hocon/config_error' class Hocon::Impl::PathBuilder def initialize @keys = [] @result = nil end def check_can_append if @result raise Hocon::ConfigError::ConfigBugOrBrokenError, "Adding to PathBuilder after getting result" end end def append_key(key) check_can_append @keys.push(key) end def append_path(path) check_can_append first = path.first remainder = path.remainder loop do @keys.push(first) if !remainder.nil? first = remainder.first remainder = remainder.remainder else break end end end def result # note: if keys is empty, we want to return nil, which is a valid # empty path if @result.nil? remainder = nil while !@keys.empty? key = @keys.pop remainder = Hocon::Impl::Path.new(key, remainder) end @result = remainder end @result end end hocon-1.3.1/lib/hocon/impl/config_node_path.rb0000644000004100000410000000215413704151031021277 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/tokens' require 'hocon/impl/abstract_config_node' class Hocon::Impl::ConfigNodePath include Hocon::Impl::AbstractConfigNode Tokens = Hocon::Impl::Tokens def initialize(path, tokens) @path = path @tokens = tokens end attr_reader :tokens def value @path end def sub_path(to_remove) period_count = 0 tokens_copy = tokens.clone (0..tokens_copy.size - 1).each do |i| if Tokens.unquoted_text?(tokens_copy[i]) && tokens_copy[i].token_text == "." period_count += 1 end if period_count == to_remove return self.class.new(@path.sub_path_to_end(to_remove), tokens_copy[i + 1..tokens_copy.size]) end end raise ConfigBugOrBrokenError, "Tried to remove too many elements from a Path node" end def first tokens_copy = tokens.clone (0..tokens_copy.size - 1).each do |i| if Tokens.unquoted_text?(tokens_copy[i]) && tokens_copy[i].token_text == "." return self.class.new(@path.sub_path(0, 1), tokens_copy[0, i]) end end self end end hocon-1.3.1/lib/hocon/impl/simple_config_document.rb0000644000004100000410000000260613704151031022527 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/parser/config_document' require 'hocon/impl/config_document_parser' require 'hocon/config_render_options' class Hocon::Impl::SimpleConfigDocument include Hocon::Parser::ConfigDocument def initialize(parsed_node, parse_options) @config_node_tree = parsed_node @parse_options = parse_options end def set_value(path, new_value) origin = Hocon::Impl::SimpleConfigOrigin.new_simple("single value parsing") reader = StringIO.new(new_value) tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, @parse_options.syntax) parsed_value = Hocon::Impl::ConfigDocumentParser.parse_value(tokens, origin, @parse_options) reader.close self.class.new(@config_node_tree.set_value(path, parsed_value, @parse_options.syntax), @parse_options) end def set_config_value(path, new_value) options = Hocon::ConfigRenderOptions.defaults options.origin_comments = false set_value(path, new_value.render(options).strip) end def remove_value(path) self.class.new(@config_node_tree.set_value(path, nil, @parse_options.syntax), @parse_options) end def has_value?(path) @config_node_tree.has_value(path) end def render @config_node_tree.render end def ==(other) other.class.ancestors.include?(Hocon::Parser::ConfigDocument) && render == other.render end def hash render.hash end end hocon-1.3.1/lib/hocon/impl/origin_type.rb0000644000004100000410000000067013704151031020342 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' module Hocon::Impl::OriginType ## for now, we only support a subset of these GENERIC = 0 FILE = 1 #URL = 2 # We don't actually support loading from the classpath / loadpath, which is # what 'RESOURCE' is about in the upstream library. However, some code paths # still flow through our simplistic implementation of `ParseableResource`, so # we need this constant. RESOURCE = 3 end hocon-1.3.1/lib/hocon/impl/simple_config_object.rb0000644000004100000410000003515413704151031022163 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/simple_config_origin' require 'hocon/impl/abstract_config_object' require 'hocon/impl/resolve_status' require 'hocon/impl/resolve_result' require 'hocon/impl/path' require 'hocon/config_error' require 'set' require 'forwardable' class Hocon::Impl::SimpleConfigObject include Hocon::Impl::AbstractConfigObject extend Forwardable ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ResolveStatus = Hocon::Impl::ResolveStatus ResolveResult = Hocon::Impl::ResolveResult SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin Path = Hocon::Impl::Path def initialize(origin, value, status = Hocon::Impl::ResolveStatus.from_values(value.values), ignores_fallbacks = false) super(origin) if value.nil? raise ConfigBugOrBrokenError, "creating config object with null map" end @value = value @resolved = (status == Hocon::Impl::ResolveStatus::RESOLVED) @ignores_fallbacks = ignores_fallbacks # Kind of an expensive debug check. Comment out? if status != Hocon::Impl::ResolveStatus.from_values(value.values) raise ConfigBugOrBrokenError, "Wrong resolved status on #{self}" end end attr_reader :value # To support accessing ConfigObjects like a hash def_delegators :@value, :[], :has_key?, :has_value?, :empty?, :size, :keys, :values, :each, :map def with_only_key(key) with_only_path(Path.new_key(key)) end def without_key(key) without_path(Path.new_key(key)) end # gets the object with only the path if the path # exists, otherwise null if it doesn't. this ensures # that if we have { a : { b : 42 } } and do # withOnlyPath("a.b.c") that we don't keep an empty # "a" object. def with_only_path_or_nil(path) key = path.first path_next = path.remainder v = value[key] if ! path_next.nil? if (!v.nil?) && (v.is_a?(Hocon::Impl::AbstractConfigObject)) v = v.with_only_path_or_nil(path_next) else # if the path has more elements but we don't have an object, # then the rest of the path does not exist. v = nil end end if v.nil? nil else self.class.new(origin, {key => v}, v.resolve_status, @ignores_fallbacks) end end def with_only_path(path) o = with_only_path_or_nil(path) if o.nil? self.class.new(origin, {}, ResolveStatus::RESOLVED, @ignores_fallbacks) else o end end def without_path(path) key = path.first remainder = path.remainder v = @value[key] if (not v.nil?) && (not remainder.nil?) && v.is_a?(Hocon::Impl::AbstractConfigObject) v = v.without_path(remainder) updated = @value.clone updated[key] = v self.class.new(origin, updated, ResolveStatus.from_values(updated.values), @ignores_fallbacks) elsif (not remainder.nil?) || v.nil? return self else smaller = Hash.new @value.each do |old_key, old_value| unless old_key == key smaller[old_key] = old_value end end self.class.new(origin, smaller, ResolveStatus.from_values(smaller.values), @ignores_fallbacks) end end def with_value(path, v) key = path.first remainder = path.remainder if remainder.nil? with_key_value(key, v) else child = @value[key] if (not child.nil?) && child.is_a?(Hocon::Impl::AbstractConfigObject) return with_key_value(key, child.with_value(remainder, v)) else subtree = v.at_path_with_origin( SimpleConfigOrigin.new_simple("with_value(#{remainder.render})"), remainder) with_key_value(key, subtree.root) end end end def with_key_value(key, v) if v.nil? raise ConfigBugOrBrokenError.new("Trying to store null ConfigValue in a ConfigObject") end new_map = Hash.new if @value.empty? new_map[key] = v else new_map = @value.clone new_map[key] = v end self.class.new(origin, new_map, ResolveStatus.from_values(new_map.values), @ignores_fallbacks) end def attempt_peek_with_partial_resolve(key) @value[key] end def new_copy_with_status(new_status, new_origin, new_ignores_fallbacks = nil) self.class.new(new_origin, @value, new_status, new_ignores_fallbacks) end def with_fallbacks_ignored() if @ignores_fallbacks self else new_copy_with_status(resolve_status, origin, true) end end def resolve_status ResolveStatus.from_boolean(@resolved) end def replace_child(child, replacement) new_children = @value.clone new_children.each do |old, old_value| if old_value.equal?(child) if replacement != nil new_children[old] = replacement else new_children.delete(old) end return self.class.new(origin, new_children, ResolveStatus.from_values(new_children.values), @ignores_fallbacks) end end raise ConfigBugOrBrokenError, "SimpleConfigObject.replaceChild did not find #{child} in #{self}" end def has_descendant?(descendant) value.values.each do |child| if child.equal?(descendant) return true end end # now do the expensive search value.values.each do |child| if child.is_a?(Hocon::Impl::Container) && child.has_descendant?(descendant) return true end end false end def ignores_fallbacks? @ignores_fallbacks end def unwrapped m = {} @value.each do |k,v| m[k] = v.unwrapped end m end def merged_with_object(abstract_fallback) require_not_ignoring_fallbacks unless abstract_fallback.is_a?(Hocon::Impl::SimpleConfigObject) raise ConfigBugOrBrokenError, "should not be reached (merging non-SimpleConfigObject)" end fallback = abstract_fallback changed = false all_resolved = true merged = {} all_keys = key_set.union(fallback.key_set) all_keys.each do |key| first = @value[key] second = fallback.value[key] kept = if first.nil? second elsif second.nil? first else first.with_fallback(second) end merged[key] = kept if first != kept changed = true end if kept.resolve_status == Hocon::Impl::ResolveStatus::UNRESOLVED all_resolved = false end end new_resolve_status = Hocon::Impl::ResolveStatus.from_boolean(all_resolved) new_ignores_fallbacks = fallback.ignores_fallbacks? if changed Hocon::Impl::SimpleConfigObject.new(Hocon::Impl::AbstractConfigObject.merge_origins([self, fallback]), merged, new_resolve_status, new_ignores_fallbacks) elsif (new_resolve_status != resolve_status) || (new_ignores_fallbacks != ignores_fallbacks?) new_copy_with_status(new_resolve_status, origin, new_ignores_fallbacks) else self end end def modify(modifier) begin modify_may_throw(modifier) rescue Hocon::ConfigError => e raise e end end def modify_may_throw(modifier) changes = nil keys.each do |k| v = value[k] # "modified" may be null, which means remove the child; # to do that we put null in the "changes" map. modified = modifier.modify_child_may_throw(k, v) if ! modified.equal?(v) if changes.nil? changes = {} end changes[k] = modified end end if changes.nil? self else modified = {} saw_unresolved = false keys.each do |k| if changes.has_key?(k) new_value = changes[k] if ! new_value.nil? modified[k] = new_value if new_value.resolve_status == ResolveStatus::UNRESOLVED saw_unresolved = true end else # remove this child; don't put it in the new map end else new_value = value[k] modified[k] = new_value if new_value.resolve_status == ResolveStatus::UNRESOLVED saw_unresolved = true end end end self.class.new(origin, modified, saw_unresolved ? ResolveStatus::UNRESOLVED : ResolveStatus::RESOLVED, @ignores_fallbacks) end end class ResolveModifier attr_accessor :context attr_reader :source def initialize(context, source) @context = context @source = source @original_restrict = context.restrict_to_child end def modify_child_may_throw(key, v) if @context.is_restricted_to_child if key == @context.restrict_to_child.first remainder = @context.restrict_to_child.remainder if remainder != nil result = @context.restrict(remainder).resolve(v, @source) @context = result.context.unrestricted.restrict(@original_restrict) return result.value else # we don't want to resolve the leaf child return v end else # not in the restrictToChild path return v end else # no restrictToChild, resolve everything result = @context.unrestricted.resolve(v, @source) @context = result.context.unrestricted.restrict(@original_restrict) result.value end end end def resolve_substitutions(context, source) if resolve_status == ResolveStatus::RESOLVED return ResolveResult.make(context, self) end source_with_parent = source.push_parent(self) begin modifier = ResolveModifier.new(context, source_with_parent) value = modify_may_throw(modifier) ResolveResult.make(modifier.context, value) rescue NotPossibleToResolve => e raise e rescue Hocon::ConfigError => e raise e end end def relativized(prefix) modifier = Class.new do include Hocon::Impl::AbstractConfigValue::NoExceptionsModifier # prefix isn't in scope inside of a def, but it is in scope inside of Class.new # so manually define a method that has access to prefix # I feel dirty define_method(:modify_child) do |key, v| v.relativized(prefix) end end modify(modifier.new) end class RenderComparator def self.all_digits?(s) s =~ /^\d+$/ end # This is supposed to sort numbers before strings, # and sort the numbers numerically. The point is # to make objects which are really list-like # (numeric indices) appear in order. def self.sort(arr) arr.sort do |a, b| a_digits = all_digits?(a) b_digits = all_digits?(b) if a_digits && b_digits Integer(a) <=> Integer(b) elsif a_digits -1 elsif b_digits 1 else a <=> b end end end end def render_value_to_sb(sb, indent, at_root, options) if empty? sb << "{}" else outer_braces = options.json? || !at_root if outer_braces inner_indent = indent + 1 sb << "{" if options.formatted? sb << "\n" end else inner_indent = indent end separator_count = 0 sorted_keys = RenderComparator.sort(keys) sorted_keys.each do |k| v = @value[k] if options.origin_comments? lines = v.origin.description.split("\n") lines.each { |l| Hocon::Impl::AbstractConfigValue.indent(sb, indent + 1, options) sb << '#' unless l.empty? sb << ' ' end sb << l sb << "\n" } end if options.comments? v.origin.comments.each do |comment| Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options) sb << "#" if !comment.start_with?(" ") sb << " " end sb << comment sb << "\n" end end Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options) v.render_to_sb(sb, inner_indent, false, k.to_s, options) if options.formatted? if options.json? sb << "," separator_count = 2 else separator_count = 1 end sb << "\n" else sb << "," separator_count = 1 end end # chop last commas/newlines # couldn't figure out a better way to chop characters off of the end of # the StringIO. This relies on making sure that, prior to returning the # final string, we take a substring that ends at sb.pos. sb.pos = sb.pos - separator_count if outer_braces if options.formatted? sb << "\n" # put a newline back if outer_braces Hocon::Impl::AbstractConfigValue.indent(sb, indent, options) end end sb << "}" end end if at_root && options.formatted? sb << "\n" end end def self.map_equals(a, b) if a.equal?(b) return true end # Hashes aren't ordered in ruby, so sort first if not a.keys.sort == b.keys.sort return false end a.keys.each do |key| if a[key] != b[key] return false end end true end def get(key) @value[key] end def self.map_hash(m) # the keys have to be sorted, otherwise we could be equal # to another map but have a different hashcode. keys = m.keys.sort value_hash = 0 keys.each do |key| value_hash += m[key].hash end 41 * (41 + keys.hash) + value_hash end def can_equal(other) other.is_a? Hocon::ConfigObject end def ==(other) # note that "origin" is deliberately NOT part of equality. # neither are other "extras" like ignoresFallbacks or resolve status. if other.is_a? Hocon::ConfigObject # optimization to avoid unwrapped() for two ConfigObject, # which is what AbstractConfigValue does. can_equal(other) && self.class.map_equals(self, other) else false end end def hash self.class.map_hash(@value) end def contains_key?(key) @value.has_key?(key) end def key_set Set.new(@value.keys) end def contains_value?(v) @value.has_value?(v) end def self.empty(origin = nil) if origin.nil? empty(Hocon::Impl::SimpleConfigOrigin.new_simple("empty config")) else self.new(origin, {}) end end def self.empty_missing(base_origin) self.new( Hocon::Impl::SimpleConfigOrigin.new_simple("#{base_origin.description} (not found)"), {}) end end hocon-1.3.1/lib/hocon/impl/resolve_memos.rb0000644000004100000410000000045513704151031020672 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl' class Hocon::Impl::ResolveMemos def initialize(memos = {}) @memos = memos end def get(key) @memos[key] end def put(key, value) copy = @memos.clone copy[key] = value Hocon::Impl::ResolveMemos.new(copy) end end hocon-1.3.1/lib/hocon/impl/simple_includer.rb0000644000004100000410000001516113704151031021171 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'hocon/impl' require 'hocon/impl/full_includer' require 'hocon/impl/url' require 'hocon/impl/config_impl' require 'hocon/config_error' require 'hocon/config_syntax' require 'hocon/impl/simple_config_object' require 'hocon/impl/simple_config_origin' require 'hocon/config_includer_file' require 'hocon/config_factory' require 'hocon/impl/parseable' class Hocon::Impl::SimpleIncluder < Hocon::Impl::FullIncluder ConfigBugorBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigIOError = Hocon::ConfigError::ConfigIOError SimpleConfigObject = Hocon::Impl::SimpleConfigObject SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin def initialize(fallback) @fallback = fallback end # ConfigIncludeContext does this for us on its options def self.clear_for_include(options) # the class loader and includer are inherited, but not this other stuff options.set_syntax(nil).set_origin_description(nil).set_allow_missing(true) end # this is the heuristic includer def include(context, name) obj = self.class.include_without_fallback(context, name) # now use the fallback includer if any and merge its result if ! (@fallback.nil?) obj.with_fallback(@fallback.include(context, name)) else obj end end # the heuristic includer in static form def self.include_without_fallback(context, name) # the heuristic is valid URL then URL, else relative to including file; # relativeTo in a file falls back to classpath inside relativeTo(). url = nil begin url = Hocon::Impl::Url.new(name) rescue Hocon::Impl::Url::MalformedUrlError => e url = nil end if !(url.nil?) include_url_without_fallback(context, url) else source = RelativeNameSource.new(context) from_basename(source, name, context.parse_options) end end # NOTE: not porting `include_url` or `include_url_without_fallback` from upstream, # because we probably won't support URL includes for now. def include_file(context, file) obj = self.class.include_file_without_fallback(context, file) # now use the fallback includer if any and merge its result if (!@fallback.nil?) && @fallback.is_a?(Hocon::ConfigIncluderFile) obj.with_fallback(@fallback).include_file(context, file) else obj end end def self.include_file_without_fallback(context, file) Hocon::ConfigFactory.parse_file_any_syntax(file, context.parse_options).root end # NOTE: not porting `include_resources` or `include_resources_without_fallback` # for now because we're not going to support looking for things on the ruby # load path for now. def with_fallback(fallback) if self.equal?(fallback) raise ConfigBugOrBrokenError, "trying to create includer cycle" elsif @fallback.equal?(fallback) self elsif @fallback.nil? self.class.new(@fallback.with_fallback(fallback)) else self.class.new(fallback) end end class NameSource def name_to_parseable(name, parse_options) raise Hocon::ConfigError::ConfigBugOrBrokenError, "name_to_parseable must be implemented by subclass (#{self.class})" end end class RelativeNameSource < NameSource def initialize(context) @context = context end def name_to_parseable(name, options) p = @context.relative_to(name) if p.nil? # avoid returning nil Hocon::Impl::Parseable.new_not_found(name, "include was not found: '#{name}'", options) else p end end end # this function is a little tricky because there are three places we're # trying to use it; for 'include "basename"' in a .conf file, for # loading app.{conf,json,properties} from classpath, and for # loading app.{conf,json,properties} from the filesystem. def self.from_basename(source, name, options) obj = nil if name.end_with?(".conf") || name.end_with?(".json") || name.end_with?(".properties") p = source.name_to_parseable(name, options) obj = p.parse(p.options.set_allow_missing(options.allow_missing?)) else conf_handle = source.name_to_parseable(name + ".conf", options) json_handle = source.name_to_parseable(name + ".json", options) got_something = false fails = [] syntax = options.syntax obj = SimpleConfigObject.empty(SimpleConfigOrigin.new_simple(name)) if syntax.nil? || (syntax == Hocon::ConfigSyntax::CONF) begin obj = conf_handle.parse(conf_handle.options.set_allow_missing(false). set_syntax(Hocon::ConfigSyntax::CONF)) got_something = true rescue ConfigIOError => e fails << e end end if syntax.nil? || (syntax == Hocon::ConfigSyntax::JSON) begin parsed = json_handle.parse(json_handle.options.set_allow_missing(false). set_syntax(Hocon::ConfigSyntax::JSON)) obj = obj.with_fallback(parsed) got_something = true rescue ConfigIOError => e fails << e end end # NOTE: skipping the upstream block here that would attempt to parse # a java properties file. if (! options.allow_missing?) && (! got_something) if Hocon::Impl::ConfigImpl.trace_loads_enabled # the individual exceptions should have been logged already # with tracing enabled Hocon::Impl::ConfigImpl.trace("Did not find '#{name}'" + " with any extension (.conf, .json, .properties); " + "exceptions should have been logged above.") end if fails.empty? # this should not happen raise ConfigBugOrBrokenError, "should not be reached: nothing found but no exceptions thrown" else sb = StringIO.new fails.each do |t| sb << t sb << ", " end raise ConfigIOError.new(SimpleConfigOrigin.new_simple(name), sb.string, fails[0]) end elsif !got_something if Hocon::Impl::ConfigImpl.trace_loads_enabled Hocon::Impl::ConfigImpl.trace("Did not find '#{name}'" + " with any extension (.conf, .json, .properties); but '#{name}'" + " is allowed to be missing. Exceptions from load attempts should have been logged above.") end end end obj end class Proxy < Hocon::Impl::FullIncluder def initialize(delegate) @delegate = delegate end ## TODO: port remaining implementation when needed end def self.make_full(includer) if includer.is_a?(Hocon::Impl::FullIncluder) includer else Proxy.new(includer) end end end hocon-1.3.1/lib/hocon/impl/simple_include_context.rb0000644000004100000410000000144213704151031022550 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/simple_includer' require 'hocon/config_include_context' require 'hocon/impl/config_impl' class Hocon::Impl::SimpleIncludeContext include Hocon::ConfigIncludeContext def initialize(parseable) @parseable = parseable end def with_parseable(parseable) if parseable.equal?(@parseable) self else self.class.new(parseable) end end def relative_to(filename) if Hocon::Impl::ConfigImpl.trace_loads_enabled Hocon::Impl::ConfigImpl.trace("Looking for '#{filename}' relative to #{@parseable}") end if ! @parseable.nil? @parseable.relative_to(filename) else nil end end def parse_options Hocon::Impl::SimpleIncluder.clear_for_include(@parseable.options) end end hocon-1.3.1/lib/hocon/impl/array_iterator.rb0000644000004100000410000000034513704151031021040 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' class Hocon::Impl::ArrayIterator def initialize(a) @a = a @index = 0 end def has_next? @index < @a.length end def next @index += 1 @a[@index - 1] end end hocon-1.3.1/lib/hocon/impl/tokens.rb0000644000004100000410000002355313704151031017322 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'hocon/impl' require 'hocon/impl/token' require 'hocon/impl/token_type' require 'hocon/impl/config_number' require 'hocon/impl/config_string' require 'hocon/impl/config_null' require 'hocon/impl/config_boolean' require 'hocon/config_error' require 'hocon/impl/resolve_status' require 'hocon/config_value_type' # FIXME the way the subclasses of Token are private with static isFoo and accessors is kind of ridiculous. class Hocon::Impl::Tokens Token = Hocon::Impl::Token TokenType = Hocon::Impl::TokenType ConfigNumber = Hocon::Impl::ConfigNumber ConfigInt = Hocon::Impl::ConfigInt ConfigDouble = Hocon::Impl::ConfigDouble ConfigString = Hocon::Impl::ConfigString ConfigNull = Hocon::Impl::ConfigNull ConfigBoolean = Hocon::Impl::ConfigBoolean ResolveStatus = Hocon::Impl::ResolveStatus ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError class Value < Token def initialize(value, orig_text = nil) super(TokenType::VALUE, value.origin, orig_text) @value = value end attr_reader :value def to_s if value.resolve_status == ResolveStatus::RESOLVED "'#{value.unwrapped}' (#{Hocon::ConfigValueType.value_type_name(value.value_type)})" else "'' ((#{Hocon::ConfigValueType.value_type_name(value.value_type)})" end end def can_equal(o) o.is_a?(Value) end def ==(other) super(other) && other.value == @value end def hash 41 * (41 + super) + value.hash end end class Line < Token def initialize(origin) super(TokenType::NEWLINE, origin) end def can_equal(other) o.is_a?(Line) end def ==(other) super(other) && other.line_number == line_number end def hash 41 * (41 + super) + line_number end def token_text "\n" end end class UnquotedText < Token def initialize(origin, s) super(TokenType::UNQUOTED_TEXT, origin) @value = s end attr_reader :value def to_s "'#{value}'" end def can_equal(o) o.is_a?(UnquotedText) end def ==(other) super(other) && other.value == @value end def hash 41 * (41 + super) + value.hash end def token_text @value end end class IgnoredWhitespace < Token def initialize(origin, s) super(TokenType::IGNORED_WHITESPACE, origin) @value = s end attr_reader :value def to_s "'#{value}' (WHITESPACE)" end def can_equal(o) o.is_a?(IgnoredWhitespace) end def ==(other) super(other) && other.value == value end def hash 41 * (41 + super) + value.hash end def token_text @value end end class Problem < Token def initialize(origin, what, message, suggest_quotes, cause) super(TokenType::PROBLEM, origin) @what = what @message = message @suggest_quotes = suggest_quotes @cause = cause end def what @what end def message @message end def suggest_quotes @suggest_quotes end def cause @cause end def to_s sb = StringIO.new sb << "'" sb << what sb << "'" sb << " (" sb << message sb << ")" sb.string end def can_equal(other) other.is_a?(Problem) end def ==(other) super(other) && other.what == @what && other.message == @message && other.suggest_quotes == @suggest_quotes && Hocon::Impl::ConfigImplUtil.equals_handling_nil?(other.cause, @cause) end def hash hashcode = 41 * (41 + super) hashcode = 41 * (hashcode + @what.hash) hashcode = 41 * (hashcode + @message.hash) hashcode = 41 * (hashcode + @suggest_quotes.hash) unless @cause.nil? hashcode = 41 * (hashcode + @cause.hash) end hashcode end end class Comment < Token def initialize(origin, text) super(TokenType::COMMENT, origin) @text = text end class DoubleSlashComment < Comment def initialize(origin, text) super(origin, text) end def token_text "//" + @text end end class HashComment < Comment def initialize(origin, text) super(origin, text) end def token_text "#" + @text end end attr_reader :text def to_s sb = StringIO.new sb << "'#" sb << text sb << "' (COMMENT)" sb.string end def can_equal(other) other.is_a?(Comment) end def ==(other) super(other) && other.text == @text end def hash hashcode = 41 * (41 + super) hashcode = 41 * (hashcode + @text.hash) hashcode end end # This is not a Value, because it requires special processing class Substitution < Token def initialize(origin, optional, expression) super(TokenType::SUBSTITUTION, origin) @optional = optional @value = expression end def optional? @optional end attr_reader :value def token_text sub_text = "" @value.each { |t| sub_text << t.token_text } "${" + (@optional ? "?" : "") + sub_text + "}" end def to_s sb = StringIO.new value.each do |t| sb << t.to_s end "'${#{sb.to_s}}'" end def can_equal(other) other.is_a?(Substitution) end def ==(other) super(other) && other.value == @value end def hash 41 * (41 + super + @value.hash) end end def self.value?(token) token.is_a?(Value) end def self.value(token) if token.is_a?(Value) token.value else raise ConfigBugOrBrokenError, "tried to get value of non-value token #{token}" end end def self.value_with_type?(t, value_type) value?(t) && (value(t).value_type == value_type) end def self.newline?(t) t.is_a?(Line) end def self.problem?(t) t.is_a?(Problem) end def self.get_problem_what(token) if token.is_a?(Problem) token.what else raise ConfigBugOrBrokenError, "tried to get problem what from #{token}" end end def self.get_problem_message(token) if token.is_a?(Problem) token.message else raise ConfigBugOrBrokenError.new("tried to get problem message from #{token}") end end def self.get_problem_suggest_quotes(token) if token.is_a?(Problem) token.suggest_quotes else raise ConfigBugOrBrokenError.new("tried to get problem suggest_quotes from #{token}") end end def self.get_problem_cause(token) if token.is_a?(Problem) token.cause else raise ConfigBugOrBrokenError.new("tried to get problem cause from #{token}") end end def self.comment?(t) t.is_a?(Comment) end def self.comment_text(token) if comment?(token) token.text else raise ConfigBugOrBrokenError, "tried to get comment text from #{token}" end end def self.unquoted_text?(token) token.is_a?(UnquotedText) end def self.unquoted_text(token) if unquoted_text?(token) token.value else raise ConfigBugOrBrokenError, "tried to get unquoted text from #{token}" end end def self.ignored_whitespace?(token) token.is_a?(IgnoredWhitespace) end def self.substitution?(token) token.is_a?(Substitution) end def self.get_substitution_path_expression(token) if token.is_a?(Substitution) token.value else raise ConfigBugOrBrokenError, "tried to get substitution from #{token}" end end def self.get_substitution_optional(token) if token.is_a?(Substitution) token.optional? else raise ConfigBugOrBrokenError, "tried to get substitution optionality from #{token}" end end START = Token.new_without_origin(TokenType::START, "start of file", "") EOF = Token.new_without_origin(TokenType::EOF, "end of file", "") COMMA = Token.new_without_origin(TokenType::COMMA, "','", ",") EQUALS = Token.new_without_origin(TokenType::EQUALS, "'='", "=") COLON = Token.new_without_origin(TokenType::COLON, "':'", ":") OPEN_CURLY = Token.new_without_origin(TokenType::OPEN_CURLY, "'{'", "{") CLOSE_CURLY = Token.new_without_origin(TokenType::CLOSE_CURLY, "'}'", "}") OPEN_SQUARE = Token.new_without_origin(TokenType::OPEN_SQUARE, "'['", "[") CLOSE_SQUARE = Token.new_without_origin(TokenType::CLOSE_SQUARE, "']'", "]") PLUS_EQUALS = Token.new_without_origin(TokenType::PLUS_EQUALS, "'+='", "+=") def self.new_line(origin) Line.new(origin) end def self.new_problem(origin, what, message, suggest_quotes, cause) Problem.new(origin, what, message, suggest_quotes, cause) end def self.new_comment_double_slash(origin, text) Comment::DoubleSlashComment.new(origin, text) end def self.new_comment_hash(origin, text) Comment::HashComment.new(origin, text) end def self.new_unquoted_text(origin, s) UnquotedText.new(origin, s) end def self.new_ignored_whitespace(origin, s) IgnoredWhitespace.new(origin, s) end def self.new_substitution(origin, optional, expression) Substitution.new(origin, optional, expression) end def self.new_value(value, orig_text = nil) Value.new(value, orig_text) end def self.new_string(origin, value, orig_text) new_value(ConfigString::Quoted.new(origin, value), orig_text) end def self.new_int(origin, value, orig_text) new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text) end def self.new_double(origin, value, orig_text) new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text) end def self.new_long(origin, value, orig_text) new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text) end def self.new_null(origin) new_value(ConfigNull.new(origin), "null") end def self.new_boolean(origin, value) new_value(ConfigBoolean.new(origin, value), value.to_s) end end hocon-1.3.1/lib/hocon/impl/config_reference.rb0000644000004100000410000001006613704151031021275 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl' require 'hocon/impl/abstract_config_value' class Hocon::Impl::ConfigReference include Hocon::Impl::Unmergeable include Hocon::Impl::AbstractConfigValue # Require these lazily, to avoid circular dependencies require 'hocon/impl/resolve_source' require 'hocon/impl/resolve_result' NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve UnresolvedSubstitutionError = Hocon::ConfigError::UnresolvedSubstitutionError attr_reader :expr, :prefix_length def initialize(origin, expr, prefix_length = 0) super(origin) @expr = expr @prefix_length = prefix_length end def unmerged_values [self] end # ConfigReference should be a firewall against NotPossibleToResolve going # further up the stack; it should convert everything to ConfigException. # This way it 's impossible for NotPossibleToResolve to "escape" since # any failure to resolve has to start with a ConfigReference. def resolve_substitutions(context, source) new_context = context.add_cycle_marker(self) begin result_with_path = source.lookup_subst(new_context, @expr, @prefix_length) new_context = result_with_path.result.context if result_with_path.result.value != nil if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "recursively resolving #{result_with_path} which was the resolution of #{expr} against #{source}", context.depth) end recursive_resolve_source = Hocon::Impl::ResolveSource.new( result_with_path.path_from_root.last, result_with_path.path_from_root) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("will recursively resolve against #{recursive_resolve_source}", context.depth) end result = new_context.resolve(result_with_path.result.value, recursive_resolve_source) v = result.value new_context = result.context else v = nil end rescue NotPossibleToResolve => e if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "not possible to resolve #{expr}, cycle involved: #{e.trace_string}", new_context.depth) end if @expr.optional v = nil else raise UnresolvedSubstitutionError.new( origin, "#{@expr} was part of a cycle of substitutions involving #{e.trace_string}", e) end end if v == nil && !@expr.optional if new_context.options.allow_unresolved ResolveResult.make(new_context.remove_cycle_marker(self), self) else raise UnresolvedSubstitutionError.new(origin, @expr.to_s) end else Hocon::Impl::ResolveResult.make(new_context.remove_cycle_marker(self), v) end end def value_type raise not_resolved end def unwrapped raise not_resolved end def new_copy(new_origin) Hocon::Impl::ConfigReference.new(new_origin, @expr, @prefix_length) end def ignores_fallbacks? false end def resolve_status Hocon::Impl::ResolveStatus::UNRESOLVED end def relativized(prefix) new_expr = @expr.change_path(@expr.path.prepend(prefix)) Hocon::Impl::ConfigReference.new(origin, new_expr, @prefix_length + prefix.length) end def can_equal(other) other.is_a? Hocon::Impl::ConfigReference end def ==(other) # note that "origin" is deliberately NOT part of equality if other.is_a? Hocon::Impl::ConfigReference can_equal(other) && @expr == other.expr end end def hash # note that "origin" is deliberately NOT part of equality @expr.hash end def render_value_to_sb(sb, indent, at_root, options) sb << @expr.to_s end def expression @expr end private def not_resolved error_message = "need to Config#resolve, see the API docs for Config#resolve; substitution not resolved: #{self}" Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil) end end hocon-1.3.1/lib/hocon/impl/config_delayed_merge.rb0000644000004100000410000002367613704151031022140 0ustar www-datawww-datarequire 'hocon/impl' require 'hocon/impl/replaceable_merge_stack' require 'hocon/impl/config_delayed_merge_object' require 'hocon/impl/config_impl' require 'hocon/impl/resolve_result' require 'hocon/impl/abstract_config_value' # # The issue here is that we want to first merge our stack of config files, and # then we want to evaluate substitutions. But if two substitutions both expand # to an object, we might need to merge those two objects. Thus, we can't ever # "override" a substitution when we do a merge; instead we have to save the # stack of values that should be merged, and resolve the merge when we evaluate # substitutions. # class Hocon::Impl::ConfigDelayedMerge include Hocon::Impl::Unmergeable include Hocon::Impl::ReplaceableMergeStack include Hocon::Impl::AbstractConfigValue ConfigImpl = Hocon::Impl::ConfigImpl ResolveResult = Hocon::Impl::ResolveResult def initialize(origin, stack) super(origin) @stack = stack if stack.empty? raise Hocon::ConfigError::ConfigBugOrBrokenError.new("creating empty delayed merge value", nil) end stack.each do |v| if v.is_a?(Hocon::Impl::ConfigDelayedMerge) || v.is_a?(Hocon::Impl::ConfigDelayedMergeObject) error_message = "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end end end attr_reader :stack def value_type error_message = "called value_type() on value with unresolved substitutions, need to Config#resolve() first, see API docs" raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil) end def unwrapped error_message = "called unwrapped() on value with unresolved substitutions, need to Config#resolve() first, see API docs" raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil) end def resolve_substitutions(context, source) self.class.resolve_substitutions(self, stack, context, source) end def self.resolve_substitutions(replaceable, stack, context, source) if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("delayed merge stack has #{stack.size} items:", context.depth) count = 0 stack.each do |v| ConfigImpl.trace("#{count}: #{v}", context.depth) count += 1 end end # to resolve substitutions, we need to recursively resolve # the stack of stuff to merge, and merge the stack so # we won't be a delayed merge anymore. If restrictToChildOrNull # is non-null, or resolve options allow partial resolves, # we may remain a delayed merge though. new_context = context count = 0 merged = nil stack.each do |stack_end| # the end value may or may not be resolved already if stack_end.is_a?(Hocon::Impl::ReplaceableMergeStack) raise ConfigBugOrBrokenError, "A delayed merge should not contain another one: #{replaceable}" elsif stack_end.is_a?(Hocon::Impl::Unmergeable) # the remainder could be any kind of value, including another # ConfigDelayedMerge remainder = replaceable.make_replacement(context, count + 1) if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("remainder portion: #{remainder}", new_context.depth) end # If, while resolving 'end' we come back to the same # merge stack, we only want to look _below_ 'end' # in the stack. So we arrange to replace the # ConfigDelayedMerge with a value that is only # the remainder of the stack below this one. if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("building sourceForEnd", new_context.depth) end # we resetParents() here because we'll be resolving "end" # against a root which does NOT contain "end" source_for_end = source.replace_within_current_parent(replaceable, remainder) if ConfigImpl.trace_substitution_enabled ConfigImpl.trace(" sourceForEnd before reset parents but after replace: #{source_for_end}", new_context.depth) end source_for_end = source_for_end.reset_parents else if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("will resolve end against the original source with parent pushed", new_context.depth) end source_for_end = source.push_parent(replaceable) end if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("sourceForEnd =#{source_for_end}", new_context.depth) end if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("Resolving highest-priority item in delayed merge #{stack_end}" + " against #{source_for_end} endWasRemoved=#{(source != source_for_end)}") end result = new_context.resolve(stack_end, source_for_end) resolved_end = result.value new_context = result.context if ! resolved_end.nil? if merged.nil? merged = resolved_end else if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("merging #{merged} with fallback #{resolved_end}", new_context.depth + 1) end merged = merged.with_fallback(resolved_end) end end count += 1 if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("stack merged, yielding: #{merged}", new_context.depth) end end ResolveResult.make(new_context, merged) end def make_replacement(context, skipping) self.class.make_replacement(context, @stack, skipping) end # static method also used by ConfigDelayedMergeObject; end may be null def self.make_replacement(context, stack, skipping) sub_stack = stack.slice(skipping..stack.size) if sub_stack.empty? if ConfigImpl.trace_substitution_enabled ConfigImpl.trace("Nothing else in the merge stack, replacing with null", context.depth) return nil end else # generate a new merge stack from only the remaining items merged = nil sub_stack.each do |v| if merged.nil? merged = v else merged = merged.with_fallback(v) end end merged end end def resolve_status Hocon::Impl::ResolveStatus::UNRESOLVED end def replace_child(child, replacement) new_stack = replace_child_in_list(stack, child, replacement) if new_stack.nil? nil else self.class.new(origin, new_stack) end end def has_descendant?(descendant) Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(stack, descendant) end def relativized(prefix) new_stack = stack.map { |o| o.relativized(prefix) } self.class.new(origin, new_stack) end # static utility shared with ConfigDelayedMergeObject def self.stack_ignores_fallbacks?(stack) last = stack[-1] last.ignores_fallbacks? end def ignores_fallbacks? self.class.stack_ignores_fallbacks?(stack) end def new_copy(new_origin) self.class.new(new_origin, stack) end def merged_with_the_unmergeable(fallback) merged_stack_with_the_unmergeable(stack, fallback) end def merged_with_object(fallback) merged_stack_with_object(stack, fallback) end def merged_with_non_object(fallback) merged_stack_with_non_object(stack, fallback) end def unmerged_values stack end def can_equal(other) other.is_a? Hocon::Impl::ConfigDelayedMerge end def ==(other) # note that "origin" is deliberately NOT part of equality if other.is_a? Hocon::Impl::ConfigDelayedMerge can_equal(other) && (@stack == other.stack || @stack.equal?(other.stack)) else false end end def hash # note that "origin" is deliberately NOT part of equality @stack.hash end def render_to_sb(sb, indent, at_root, at_key, options) self.class.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options) end # static method also used by ConfigDelayedMergeObject. def self.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options) comment_merge = options.comments if comment_merge sb << "# unresolved merge of #{stack.size} values follows (\n" if at_key.nil? self.indent(sb, indent, options) sb << "# this unresolved merge will not be parseable because it's at the root of the object\n" self.indent(sb, indent, options) sb << "# the HOCON format has no way to list multiple root objects in a single file\n" end end reversed = stack.reverse i = 0 reversed.each do |v| if comment_merge self.indent(sb, indent, options) if !at_key.nil? rendered_key = Hocon::Impl::ConfigImplUtil.render_json_string(at_key) sb << "# unmerged value #{i} for key #{rendered_key}" else sb << "# unmerged value #{i} from " end i += 1 sb << v.origin.description sb << "\n" v.origin.comments.each do |comment| self.indent(sb, indent, options) sb << "# " sb << comment sb << "\n" end end Hocon::Impl::AbstractConfigValue.indent(sb, indent, options) if !at_key.nil? sb << Hocon::Impl::ConfigImplUtil.render_json_string(at_key) if options.formatted sb << ": " else sb << ":" end end v.render_value_to_sb(sb, indent, at_root, options) sb << "," if options.formatted sb.append "\n" end end # chop comma or newline # couldn't figure out a better way to chop characters off of the end of # the StringIO. This relies on making sure that, prior to returning the # final string, we take a substring that ends at sb.pos. sb.pos = sb.pos - 1 if options.formatted sb.pos = sb.pos - 1 sb << "\n" end if comment_merge self.indent(sb, indent, options) sb << "# ) end of unresolved merge\n" end end end hocon-1.3.1/lib/hocon/impl/config_impl_util.rb0000644000004100000410000000500013704151031021325 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'stringio' class Hocon::Impl::ConfigImplUtil def self.equals_handling_nil?(a, b) # This method probably doesn't make any sense in ruby... not sure if a.nil? && !b.nil? false elsif !a.nil? && b.nil? false # in ruby, the == and .equal? are the opposite of what they are in Java elsif a.equal?(b) true else a == b end end # # This is public ONLY for use by the "config" package, DO NOT USE this ABI # may change. # def self.render_json_string(s) sb = StringIO.new sb << '"' s.chars.each do |c| case c when '"' then sb << "\\\"" when "\\" then sb << "\\\\" when "\n" then sb << "\\n" when "\b" then sb << "\\b" when "\f" then sb << "\\f" when "\r" then sb << "\\r" when "\t" then sb << "\\t" else if c =~ /[[:cntrl:]]/ sb << ("\\u%04x" % c) else sb << c end end end sb << '"' sb.string end def self.render_string_unquoted_if_possible(s) # this can quote unnecessarily as long as it never fails to quote when # necessary if s.length == 0 return render_json_string(s) end # if it starts with a hyphen or number, we have to quote # to ensure we end up with a string and not a number first = s.chars.first if (first =~ /[[:digit:]]/) || (first == '-') return render_json_string(s) end # only unquote if it's pure alphanumeric s.chars.each do |c| unless (c =~ /[[:alnum:]]/) || (c == '-') return render_json_string(s) end end s end def self.join_path(*elements) Hocon::Impl::Path.from_string_list(elements).render end def self.split_path(path) p = Hocon::Impl::Path.new_path(path) elements = [] until p.nil? elements << p.first p = p.remainder end elements end def self.whitespace?(c) # this implementation is *not* a port of the java code, because it relied on # the method java.lang.Character#isWhitespace. This is probably # insanely slow (running a regex against every single character in the # file). c =~ /[[:space:]]/ end def self.unicode_trim(s) # this implementation is *not* a port of the java code. Ruby can strip # unicode whitespace much easier than Java can, and relies on a lot of # Java functions that don't really have straight equivalents in Ruby. s.gsub(/[:space]/, ' ') s.strip end end hocon-1.3.1/lib/hocon/impl/config_node_root.rb0000644000004100000410000000417213704151031021330 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_node_array' require 'hocon/impl/config_node_complex_value' require 'hocon/impl/config_node_object' class Hocon::Impl::ConfigNodeRoot include Hocon::Impl::ConfigNodeComplexValue def initialize(children, origin) super(children) @origin = origin end def new_node(nodes) raise Hocon::ConfigError::ConfigBugOrBrokenError, "Tried to indent the root object" end def value @children.each do |node| if node.is_a?(Hocon::Impl::ConfigNodeComplexValue) return node end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value" end def set_value(desired_path, value, flavor) children_copy = @children.clone children_copy.each_with_index do |node, index| if node.is_a?(Hocon::Impl::ConfigNodeComplexValue) if node.is_a?(Hocon::Impl::ConfigNodeArray) raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array." elsif node.is_a?(Hocon::Impl::ConfigNodeObject) if value.nil? children_copy[index] = node.remove_value_on_path(desired_path, flavor) else children_copy[index] = node.set_value_on_path(desired_path, value, flavor) end return self.class.new(children_copy, @origin) end end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value" end def has_value(desired_path) path = Hocon::Impl::PathParser.parse_path(desired_path) @children.each do |node| if node.is_a?(Hocon::Impl::ConfigNodeComplexValue) if node.is_a?(Hocon::Impl::ConfigNodeArray) raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array." elsif node.is_a?(Hocon::Impl::ConfigNodeObject) return node.has_value(path) end end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value" end end hocon-1.3.1/lib/hocon/impl/resolve_source.rb0000644000004100000410000002615613704151031021060 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' require 'hocon/impl' require 'hocon/impl/config_impl' require 'hocon/impl/container' class Hocon::Impl::ResolveSource ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError # 'path_from_root' is used for knowing the chain of parents we used to get here. # null if we should assume we are not a descendant of the root. # the root itself should be a node in this if non-null. attr_accessor :root, :path_from_root def initialize(root, path_from_root = nil) @root = root @path_from_root = path_from_root end # as a side effect, findInObject() will have to resolve all parents of the # child being peeked, but NOT the child itself.Caller has to resolve # the child itself if needed.ValueWithPath.value can be null but # the ValueWithPath instance itself should not be. def find_in_object(obj, context, path) # resolve ONLY portions of the object which are along our path if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("*** finding '#{path}' in #{obj}") end restriction = context.restrict_to_child partially_resolved = context.restrict(path).resolve(obj, self.class.new(obj)) new_context = partially_resolved.context.restrict(restriction) if partially_resolved.value.is_a?(Hocon::Impl::AbstractConfigObject) pair = self.class.find_in_object_impl(partially_resolved.value, path) ResultWithPath.new(Hocon::Impl::ResolveResult.make(new_context, pair.value), pair.path_from_root) else raise ConfigBugOrBrokenError.new("resolved object to non-object " + obj + " to " + partially_resolved) end end def lookup_subst(context, subst, prefix_length) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("searching for #{subst}", context.depth) end if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("#{subst} - looking up relative to file it occurred in", context.depth) end # First we look up the full path, which means relative to the # included file if we were not a root file result = find_in_object(@root, context, subst.path) if result.result.value == nil # Then we want to check relative to the root file.We don 't # want the prefix we were included at to be used when looking # up env variables either. unprefixed = subst.path.sub_path_to_end(prefix_length) if prefix_length > 0 if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( unprefixed + " - looking up relative to parent file", result.result.context.depth) end result = find_in_object(@root, result.result.context, unprefixed) end if result.result.value == nil && result.result.context.options.use_system_environment if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "#{unprefixed} - looking up in system environment", result.result.context.depth) end result = find_in_object(Hocon::Impl::ConfigImpl.env_variables_as_config_object, context, unprefixed) end end if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "resolved to #{result}", result.result.context.depth) end result end def push_parent(parent) unless parent raise ConfigBugOrBrokenError.new("can't push null parent") end if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("pushing parent #{parent} ==root #{(parent == root)} onto #{self}") end if @path_from_root == nil if parent.equal?(@root) return self.class.new(@root, Node.new(parent)) else if Hocon::Impl::ConfigImpl.trace_substitution_enabled # this hasDescendant check is super-expensive so it's a # trace message rather than an assertion if @root.has_descendant?(parent) Hocon::Impl::ConfigImpl.trace( "***** BUG ***** tried to push parent #{parent} without having a path to it in #{self}") end end # ignore parents if we aren't proceeding from the # root return self end else parent_parent = @path_from_root.head if Hocon::Impl::ConfigImpl.trace_substitution_enabled # this hasDescendant check is super-expensive so it's a # trace message rather than an assertion if parent_parent != nil && !parent_parent.has_descendant?(parent) Hocon::Impl::ConfigImpl.trace( "***** BUG ***** trying to push non-child of #{parent_parent}, non-child was #{parent}") end end self.class.new(@root, @path_from_root.prepend(parent)) end end def reset_parents if @path_from_root == nil this else self.class.new(@root) end end def replace_current_parent(old, replacement) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("replaceCurrentParent old #{old}@#{old.hash} replacement " + "#{replacement}@#{old.hash} in #{self}") end if old.equal?(replacement) self elsif @path_from_root != nil new_path = self.class.replace(@path_from_root, old, replacement) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("replaced #{old} with #{replacement} in #{self}") Hocon::Impl::ConfigImpl.trace("path was: #{@path_from_root} is now #{new_path}") end # if we end up nuking the root object itself, we replace it with an # empty root if new_path != nil return self.class.new(new_path.last, new_path) else return self.class.new(Hocon::Impl::SimpleConfigObject.empty) end else if old.equal?(@root) return self.class.new(root_must_be_obj(replacement)) else raise ConfigBugOrBrokenError.new("attempt to replace root #{root} with #{replacement}") end end end # replacement may be null to delete def replace_within_current_parent(old, replacement) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("replaceWithinCurrentParent old #{old}@#{old.hash}" + " replacement #{replacement}@#{old.hash} in #{self}") end if old.equal?(replacement) self elsif @path_from_root != nil parent = @path_from_root.head new_parent = parent.replace_child(old, replacement) return replace_current_parent(parent, new_parent.is_a?(Hocon::Impl::Container) ? new_parent : nil) else if old.equal?(@root) && replacement.is_a?(Hocon::Impl::Container) return self.class.new(root_must_be_obj(replacement)) else raise ConfigBugOrBrokenError.new("replace in parent not possible #{old} with #{replacement}" + " in #{self}") end end end def to_s "ResolveSource(root=#{@root}, pathFromRoot=#{@path_from_root})" end # a persistent list class Node attr_reader :next_node, :value def initialize(value, next_node = nil) @value = value @next_node = next_node end def prepend(value) Node.new(value, self) end def head @value end def tail @next_node end def last i = self while i.next_node != nil i = i.next_node end i.value end def reverse if @next_node == nil self else reversed = Node.new(@value) i = @next_node while i != nil reversed = reversed.prepend(i.value) i = i.next_node end reversed end end def to_s sb = "" sb << "[" to_append_value = self.reverse while to_append_value != nil sb << to_append_value.value.to_s if to_append_value.next_node != nil sb << " <= " end to_append_value = to_append_value.next_node end sb << "]" sb end end # value is allowed to be null class ValueWithPath attr_reader :value, :path_from_root def initialize(value, path_from_root) @value = value @path_from_root = path_from_root end def to_s "ValueWithPath(value=" + @value + ", pathFromRoot=" + @path_from_root + ")" end end class ResultWithPath attr_reader :result, :path_from_root def initialize(result, path_from_root) @result = result @path_from_root = path_from_root end def to_s "ResultWithPath(result=#{@result}, pathFromRoot=#{@path_from_root})" end end private def root_must_be_obj(value) if value.is_a?(Hocon::Impl::AbstractConfigObject) value else Hocon::Impl::SimpleConfigObject.empty end end def self.find_in_object_impl(obj, path, parents = nil) begin # we 'll fail if anything along the path can' t # be looked at without resolving. find_in_object_impl_impl(obj, path, nil) rescue ConfigNotResolvedError => e raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e) end end def self.find_in_object_impl_impl(obj, path, parents) key = path.first remainder = path.remainder if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("*** looking up '#{key}' in #{obj}") end v = obj.attempt_peek_with_partial_resolve(key) new_parents = parents == nil ? Node.new(obj) : parents.prepend(obj) if remainder == nil ValueWithPath.new(v, new_parents) else if v.is_a?(Hocon::Impl::AbstractConfigObject) find_in_object_impl_impl(v, remainder, new_parents) else ValueWithPath.new(nil, new_parents) end end end # returns null if the replacement results in deleting all the nodes. def self.replace(list, old, replacement) child = list.head unless child.equal?(old) raise ConfigBugOrBrokenError.new("Can only replace() the top node we're resolving; had " + child + " on top and tried to replace " + old + " overall list was " + list) end parent = list.tail == nil ? nil : list.tail.head if replacement == nil || !replacement.is_a?(Hocon::Impl::Container) if parent == nil return nil else # we are deleting the child from the stack of containers # because it's either going away or not a container new_parent = parent.replace_child(old, nil) return replace(list.tail, parent, new_parent) end else # we replaced the container with another container if parent == nil return Node.new(replacement) else new_parent = parent.replace_child(old, replacement) new_tail = replace(list.tail, parent, new_parent) if new_tail != nil return new_tail.prepend(replacement) else return Node.new(replacement) end end end end end hocon-1.3.1/lib/hocon/impl/config_node_include.rb0000644000004100000410000000122613704151031021765 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_error' require 'hocon/impl/abstract_config_node' require 'hocon/impl/config_node_simple_value' class Hocon::Impl::ConfigNodeInclude include Hocon::Impl::AbstractConfigNode def initialize(children, kind) @children = children @kind = kind end attr_reader :kind, :children def tokens tokens = [] @children.each do |child| tokens += child.tokens end tokens end def name @children.each do |child| if child.is_a?(Hocon::Impl::ConfigNodeSimpleValue) return Hocon::Impl::Tokens.value(child.token).unwrapped end end nil end endhocon-1.3.1/lib/hocon/impl/substitution_expression.rb0000644000004100000410000000122413704151031023041 0ustar www-datawww-datarequire 'hocon/impl' class Hocon::Impl::SubstitutionExpression def initialize(path, optional) @path = path @optional = optional end attr_reader :path, :optional def change_path(new_path) if new_path == @path self else Hocon::Impl::SubstitutionExpression.new(new_path, @optional) end end def to_s "${#{@optional ? "?" : ""}#{@path.render}}" end def ==(other) if other.is_a? Hocon::Impl::SubstitutionExpression other.path == @path && other.optional == @optional else false end end def hash h = 41 * (41 + @path.hash) h = 41 * (h + (optional ? 1 : 0)) h end endhocon-1.3.1/lib/hocon/impl/abstract_config_value.rb0000644000004100000410000002621513704151031022341 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'stringio' require 'hocon/config_render_options' require 'hocon/config_object' require 'hocon/impl/resolve_status' require 'hocon/impl/resolve_result' require 'hocon/impl/unmergeable' require 'hocon/impl/config_impl_util' require 'hocon/config_error' require 'hocon/config_value' ## ## Trying very hard to avoid a parent reference in config values; when you have ## a tree like this, the availability of parent() tends to result in a lot of ## improperly-factored and non-modular code. Please don't add parent(). ## module Hocon::Impl::AbstractConfigValue include Hocon::ConfigValue ConfigImplUtil = Hocon::Impl::ConfigImplUtil ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ResolveStatus = Hocon::Impl::ResolveStatus def initialize(origin) @origin = origin end attr_reader :origin # This exception means that a value is inherently not resolveable, at the # moment the only known cause is a cycle of substitutions. This is a # checked exception since it's internal to the library and we want to be # sure we handle it before passing it out to public API. This is only # supposed to be thrown by the target of a cyclic reference and it's # supposed to be caught by the ConfigReference looking up that reference, # so it should be impossible for an outermost resolve() to throw this. # # Contrast with ConfigException.NotResolved which just means nobody called # resolve(). class NotPossibleToResolve < Exception def initialize(context) super("was not possible to resolve") @trace_string = context.trace_string end attr_reader :trace_string end # Called only by ResolveContext.resolve # # @param context # state of the current resolve # @param source # where to look up values # @return a new value if there were changes, or this if no changes def resolve_substitutions(context, source) Hocon::Impl::ResolveResult.make(context, self) end def resolve_status Hocon::Impl::ResolveStatus::RESOLVED end def self.replace_child_in_list(list, child, replacement) i = 0 while (i < list.size) && (! list[i].equal?(child)) i += 1 end if (i == list.size) raise ConfigBugOrBrokenError, "tried to replace #{child} which is not in #{list}" end new_stack = list.clone if ! replacement.nil? new_stack[i] = replacement else new_stack.delete(i) end if new_stack.empty? nil else new_stack end end def self.has_descendant_in_list?(list, descendant) list.each do |v| if v.equal?(descendant) return true end end # now the expensive traversal list.each do |v| if v.is_a?(Hocon::Impl::Container) && v.has_descendant?(descendant) return true end end false end # This is used when including one file in another; the included file is # relativized to the path it's included into in the parent file. The point # is that if you include a file at foo.bar in the parent, and the included # file as a substitution ${a.b.c}, the included substitution now needs to # be ${foo.bar.a.b.c} because we resolve substitutions globally only after # parsing everything. # # @param prefix # @return value relativized to the given path or the same value if nothing # to do def relativized(prefix) self end module NoExceptionsModifier def modify_child_may_throw(key_or_nil, v) begin modify_child(key_or_nil, v) rescue Hocon::ConfigError => e raise e end end end def to_fallback_value self end def new_copy(origin) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigValue should provide their own implementation of `new_copy` (#{self.class})" end # this is virtualized rather than a field because only some subclasses # really need to store the boolean, and they may be able to pack it # with another boolean to save space. def ignores_fallbacks? # if we are not resolved, then somewhere in this value there's # a substitution that may need to look at the fallbacks. resolve_status == Hocon::Impl::ResolveStatus::RESOLVED end def with_fallbacks_ignored if ignores_fallbacks? self else raise ConfigBugOrBrokenError, "value class doesn't implement forced fallback-ignoring #{self}" end end # the withFallback() implementation is supposed to avoid calling # mergedWith* if we're ignoring fallbacks. def require_not_ignoring_fallbacks if ignores_fallbacks? raise ConfigBugOrBrokenError, "method should not have been called with ignoresFallbacks=true #{self.class.name}" end end def construct_delayed_merge(origin, stack) # TODO: this might not work because ConfigDelayedMerge inherits # from this class, so we can't `require` it from this file require 'hocon/impl/config_delayed_merge' Hocon::Impl::ConfigDelayedMerge.new(origin, stack) end def merged_stack_with_the_unmergeable(stack, fallback) require_not_ignoring_fallbacks # if we turn out to be an object, and the fallback also does, # then a merge may be required; delay until we resolve. new_stack = stack.clone new_stack.concat(fallback.unmerged_values) # TODO: this might not work because AbstractConfigObject inherits # from this class, so we can't `require` it from this file construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack) end def delay_merge(stack, fallback) # if we turn out to be an object, and the fallback also does, # then a merge may be required. # if we contain a substitution, resolving it may need to look # back to the fallback new_stack = stack.clone new_stack << fallback # TODO: this might not work because AbstractConfigObject inherits # from this class, so we can't `require` it from this file construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack) end def merged_stack_with_object(stack, fallback) require_not_ignoring_fallbacks # TODO: this might not work because AbstractConfigObject inherits # from this class, so we can't `require` it from this file if self.is_a?(Hocon::Impl::AbstractConfigObject) raise ConfigBugOrBrokenError, "Objects must reimplement merged_with_object" end merged_stack_with_non_object(stack, fallback) end def merged_stack_with_non_object(stack, fallback) require_not_ignoring_fallbacks if resolve_status == ResolveStatus::RESOLVED # falling back to a non-object doesn't merge anything, and also # prohibits merging any objects that we fall back to later. # so we have to switch to ignoresFallbacks mode. with_fallbacks_ignored else # if unresolved we may have to look back to fallbacks as part of # the resolution process, so always delay delay_merge(stack, fallback) end end def merged_with_the_unmergeable(fallback) require_not_ignoring_fallbacks merged_stack_with_the_unmergeable([self], fallback) end def merged_with_object(fallback) require_not_ignoring_fallbacks merged_stack_with_object([self], fallback) end def merged_with_non_object(fallback) require_not_ignoring_fallbacks merged_stack_with_non_object([self], fallback) end def with_origin(origin) if @origin.equal?(origin) self else new_copy(origin) end end def with_fallback(mergeable) if ignores_fallbacks? self else other = mergeable.to_fallback_value if other.is_a?(Hocon::Impl::Unmergeable) merged_with_the_unmergeable(other) # TODO: this probably isn't going to work because AbstractConfigObject inherits # from this class, so we can't `require` it from this file elsif other.is_a?(Hocon::Impl::AbstractConfigObject) merged_with_object(other) else merged_with_non_object(other) end end end def can_equal(other) other.is_a?(Hocon::Impl::AbstractConfigValue) end def ==(other) # note that "origin" is deliberately NOT part of equality if other.is_a?(Hocon::Impl::AbstractConfigValue) can_equal(other) && value_type == other.value_type && ConfigImplUtil.equals_handling_nil?(unwrapped, other.unwrapped) else false end end def hash # note that "origin" is deliberately NOT part of equality unwrapped_value = unwrapped if unwrapped_value.nil? 0 else unwrapped_value.hash end end def to_s sb = StringIO.new render_to_sb(sb, 0, true, nil, Hocon::ConfigRenderOptions.concise) "#{self.class.name.split('::').last}(#{sb.string})" end def inspect to_s end def self.indent(sb, indent_size, options) if options.formatted? remaining = indent_size while remaining > 0 sb << " " remaining -= 1 end end end def render_to_sb(sb, indent, at_root, at_key, options) if !at_key.nil? rendered_key = if options.json? ConfigImplUtil.render_json_string(at_key) else ConfigImplUtil.render_string_unquoted_if_possible(at_key) end sb << rendered_key if options.json? if options.formatted? sb << ": " else sb << ":" end else case options.key_value_separator when :colon sb << ": " else sb << "=" end end end render_value_to_sb(sb, indent, at_root, options) end # to be overridden by subclasses def render_value_to_sb(sb, indent, at_root, options) u = unwrapped sb << u.to_s end def render(options = Hocon::ConfigRenderOptions.defaults) sb = StringIO.new render_to_sb(sb, 0, true, nil, options) # We take a substring that ends at sb.pos, because we've been decrementing # sb.pos at various points in the code as a means to remove characters from # the end of the StringIO sb.string[0, sb.pos] end # toString() is a debugging-oriented string but this is defined # to create a string that would parse back to the value in JSON. # It only works for primitive values (that would be a single # which are auto-converted to strings when concatenating with # other strings or by the DefaultTransformer. def transform_to_string nil end def at_key(key) at_key_with_origin(Hocon::Impl::SimpleConfigOrigin.new_simple("at_key(#{key})"), key) end # Renamed this to be consistent with the other at_key* overloaded methods def at_key_with_origin(origin, key) m = {key=>self} Hocon::Impl::SimpleConfigObject.new(origin, m).to_config end # In java this is an overloaded version of atPath def at_path_with_origin(origin, path) parent = path.parent result = at_key_with_origin(origin, path.last) while not parent.nil? do key = parent.last result = result.at_key_with_origin(origin, key) parent = parent.parent end result end def at_path(path_expression) origin = Hocon::Impl::SimpleConfigOrigin.new_simple("at_path(#{path_expression})") at_path_with_origin(origin, Hocon::Impl::Path.new_path(path_expression)) end end hocon-1.3.1/lib/hocon/impl/abstract_config_node_value.rb0000644000004100000410000000063113704151031023340 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_node' # This essentially exists in the upstream so we can ensure only certain types of # config nodes can be passed into some methods. That's not a problem in Ruby, so this is # unnecessary, but it seems best to keep it around for consistency module Hocon::Impl::AbstractConfigNodeValue include Hocon::Impl::AbstractConfigNode endhocon-1.3.1/lib/hocon/impl/simple_config_list.rb0000644000004100000410000002024713704151031021665 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/resolve_status' require 'hocon/config_value_type' require 'hocon/config_error' require 'hocon/impl/abstract_config_object' require 'forwardable' require 'hocon/impl/unsupported_operation_error' require 'hocon/impl/resolve_result' require 'hocon/impl/container' require 'hocon/config_list' class Hocon::Impl::SimpleConfigList include Hocon::Impl::Container include Hocon::ConfigList include Hocon::Impl::AbstractConfigValue extend Forwardable ResolveStatus = Hocon::Impl::ResolveStatus ResolveResult = Hocon::Impl::ResolveResult ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError def initialize(origin, value, status = ResolveStatus.from_values(value)) super(origin) @value = value @resolved = (status == ResolveStatus::RESOLVED) # kind of an expensive debug check (makes this constructor pointless) if status != ResolveStatus.from_values(value) raise ConfigBugOrBrokenError, "SimpleConfigList created with wrong resolve status: #{self}" end end attr_reader :value def_delegators :@value, :[], :include?, :empty?, :size, :index, :rindex, :each, :map def value_type Hocon::ConfigValueType::LIST end def unwrapped @value.map { |v| v.unwrapped } end def resolve_status ResolveStatus.from_boolean(@resolved) end def replace_child(child, replacement) new_list = replace_child_in_list(@value, child, replacement) if new_list.nil? nil else # we use the constructor flavor that will recompute the resolve status SimpleConfigList.new(origin, new_list) end end def has_descendant?(descendant) Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(@value, descendant) end def modify(modifier, new_resolve_status) begin modify_may_throw(modifier, new_resolve_status) rescue Hocon::ConfigError => e raise e end end def modify_may_throw(modifier, new_resolve_status) # lazy-create for optimization changed = nil i = 0 @value.each { |v| modified = modifier.modify_child_may_throw(nil, v) # lazy-create the new list if required if changed == nil && !modified.equal?(v) changed = [] j = 0 while j < i changed << @value[j] j += 1 end end # once the new list is created, all elements # have to go in it.if modifyChild returned # null, we drop that element. if changed != nil && modified != nil changed << modified end i += 1 } if changed != nil if new_resolve_status != nil self.class.new(origin, changed, new_resolve_status) else self.class.new(origin, changed) end else self end end class ResolveModifier attr_reader :context, :source def initialize(context, source) @context = context @source = source end def modify_child_may_throw(key, v) result = @context.resolve(v, source) @context = result.context result.value end end def resolve_substitutions(context, source) if @resolved return Hocon::Impl::ResolveResult.make(context, self) end if context.is_restricted_to_child # if a list restricts to a child path, then it has no child paths, # so nothing to do. Hocon::Impl::ResolveResult.make(context, self) else begin modifier = ResolveModifier.new(context, source.push_parent(self)) value = modify_may_throw(modifier, context.options.allow_unresolved ? nil : ResolveStatus::RESOLVED) Hocon::Impl::ResolveResult.make(modifier.context, value) rescue NotPossibleToResolve => e raise e rescue RuntimeError => e raise e rescue Exception => e raise ConfigBugOrBrokenError.new("unexpected exception", e) end end end def relativized(prefix) modifier = Class.new do include Hocon::Impl::AbstractConfigValue::NoExceptionsModifier # prefix isn't in scope inside of a def, but it is in scope inside of Class.new # so manually define a method that has access to prefix # I feel dirty define_method(:modify_child) do |key, v| v.relativized(prefix) end end modify(modifier.new, resolve_status) end def can_equal(other) other.is_a?(self.class) end def ==(other) # note that "origin" is deliberately NOT part of equality if other.is_a?(self.class) # optimization to avoid unwrapped() for two ConfigList can_equal(other) && (value.equal?(other.value) || (value == other.value)) else false end end def hash # note that "origin" is deliberately NOT part of equality value.hash end def render_value_to_sb(sb, indent_size, at_root, options) if @value.empty? sb << "[]" else sb << "[" if options.formatted? sb << "\n" end @value.each do |v| if options.origin_comments? lines = v.origin.description.split("\n") lines.each do |l| Hocon::Impl::AbstractConfigValue.indent(sb, indent_size + 1, options) sb << "# " sb << l sb << "\n" end end if options.comments? v.origin.comments.each do |comment| sb << "# " sb << comment sb << "\n" end end Hocon::Impl::AbstractConfigValue.indent(sb, indent_size + 1, options) v.render_value_to_sb(sb, indent_size + 1, at_root, options) sb << "," if options.formatted? sb << "\n" end end # couldn't figure out a better way to chop characters off of the end of # the StringIO. This relies on making sure that, prior to returning the # final string, we take a substring that ends at sb.pos. sb.pos = sb.pos - 1 # chop or newline if options.formatted? sb.pos = sb.pos - 1 # also chop comma sb << "\n" Hocon::Impl::AbstractConfigValue.indent(sb, indent_size, options) end sb << "]" end end def contains?(o) value.include?(o) end def include_all?(value_list) value_list.all? { |v| @value.include?(v)} end def contains_all?(c) include_all?(c) end def get(index) value[index] end def index_of(o) value.index(o) end def is_empty empty? end # Skipping upstream definition of "iterator", because that's not really a thing # in Ruby. def last_index_of(o) value.rindex(o) end # skipping upstream definitions of "wrapListIterator", "listIterator", and # "listIterator(int)", because those don't really apply in Ruby. def sub_list(from_index, to_index) value[from_index..to_index] end def to_array value end def we_are_immutable(method) Hocon::Impl::UnsupportedOperationError.new("ConfigList is immutable, you can't call List. '#{method}'") end def add(e) raise we_are_immutable("add") end def add_at(index, element) raise we_are_immutable("add_at") end def add_all(c) raise we_are_immutable("add_all") end def add_all_at(index, c) raise we_are_immutable("add_all_at") end def clear raise we_are_immutable("clear") end def remove(o) raise we_are_immutable("remove") end def remove_at(i) raise we_are_immutable("remove_at") end def delete(o) raise we_are_immutable("delete") end def remove_all(c) raise we_are_immutable("remove_all") end def retain_all(c) raise we_are_immutable("retain_all") end def set(index, element) raise we_are_immutable("set") end def []=(index, element) raise we_are_immutable("[]=") end def push(e) raise we_are_immutable("push") end def <<(e) raise we_are_immutable("<<") end def new_copy(origin) Hocon::Impl::SimpleConfigList.new(origin, @value) end def concatenate(other) combined_origin = Hocon::Impl::SimpleConfigOrigin.merge_two_origins(origin, other.origin) combined = value + other.value Hocon::Impl::SimpleConfigList.new(combined_origin, combined) end # Skipping upstream "writeReplace" until we see that we need it for something def with_origin(origin) super(origin) end end hocon-1.3.1/lib/hocon/impl/tokenizer.rb0000644000004100000410000004246213704151031020031 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_impl_util' require 'hocon/impl/tokens' require 'hocon/config_error' require 'stringio' require 'forwardable' class Hocon::Impl::Tokenizer Tokens = Hocon::Impl::Tokens ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError class TokenizerProblemError < StandardError def initialize(problem) @problem = problem end def problem @problem end end def self.as_string(codepoint) if codepoint == "\n" "newline" elsif codepoint == "\t" "tab" elsif codepoint == -1 "end of file" elsif codepoint =~ /[[:cntrl:]]/ "control character 0x%x" % codepoint else "%c" % codepoint end end # Tokenizes a Reader. Does not close the reader; you have to arrange to do # that after you're done with the returned iterator. def self.tokenize(origin, input, syntax) TokenIterator.new(origin, input, syntax != Hocon::ConfigSyntax::JSON) end def self.render(tokens) rendered_text = "" while (t = tokens.next) rendered_text << t.token_text end rendered_text end class TokenIterator class WhitespaceSaver def initialize @whitespace = StringIO.new @last_token_was_simple_value = false end def add(c) @whitespace << c end def check(t, base_origin, line_number) if TokenIterator.simple_value?(t) next_is_a_simple_value(base_origin, line_number) else next_is_not_a_simple_value(base_origin, line_number) end end private # called if the next token is not a simple value; # discards any whitespace we were saving between # simple values. def next_is_not_a_simple_value(base_origin, line_number) @last_token_was_simple_value = false create_whitespace_token_from_saver(base_origin, line_number) end # called if the next token IS a simple value, # so creates a whitespace token if the previous # token also was. def next_is_a_simple_value(base_origin, line_number) t = create_whitespace_token_from_saver(base_origin, line_number) @last_token_was_simple_value = true unless @last_token_was_simple_value t end def create_whitespace_token_from_saver(base_origin, line_number) return nil unless @whitespace.length > 0 if (@last_token_was_simple_value) t = Tokens.new_unquoted_text( Hocon::Impl::Tokenizer::TokenIterator.line_origin(base_origin, line_number), String.new(@whitespace.string) ) else t = Tokens.new_ignored_whitespace( Hocon::Impl::Tokenizer::TokenIterator.line_origin(base_origin, line_number), String.new(@whitespace.string) ) end @whitespace.string = "" t end end def initialize(origin, input, allow_comments) @origin = origin @input = input @allow_comments = allow_comments @buffer = [] @line_number = 1 @line_origin = @origin.with_line_number(@line_number) @tokens = [] @tokens << Tokens::START @whitespace_saver = WhitespaceSaver.new end # this should ONLY be called from nextCharSkippingComments # or when inside a quoted string, or when parsing a sequence # like ${ or +=, everything else should use # nextCharSkippingComments(). def next_char_raw if @buffer.empty? begin @input.readchar.chr rescue EOFError -1 end else @buffer.pop end end def put_back(c) if @buffer.length > 2 raise ConfigBugOrBrokenError, "bug: putBack() three times, undesirable look-ahead" end @buffer.push(c) end def self.whitespace?(c) Hocon::Impl::ConfigImplUtil.whitespace?(c) end def self.whitespace_not_newline?(c) (c != "\n") and (Hocon::Impl::ConfigImplUtil.whitespace?(c)) end def start_of_comment?(c) if c == -1 false else if @allow_comments if c == '#' true elsif c == '/' maybe_second_slash = next_char_raw # we want to predictably NOT consume any chars put_back(maybe_second_slash) if maybe_second_slash == '/' true else false end end else false end end end # get next char, skipping non-newline whitespace def next_char_after_whitespace(saver) while true c = next_char_raw if c == -1 return -1 else if self.class.whitespace_not_newline?(c) saver.add(c) else return c end end end end def self.problem(origin, what, message, suggest_quotes, cause) if what.nil? || message.nil? raise ConfigBugOrBrokenError.new("internal error, creating bad TokenizerProblemError") end TokenizerProblemError.new(Tokens.new_problem(origin, what, message, suggest_quotes, cause)) end def self.line_origin(base_origin, line_number) base_origin.with_line_number(line_number) end # ONE char has always been consumed, either the # or the first /, but not # both slashes def pull_comment(first_char) double_slash = false if first_char == '/' discard = next_char_raw if discard != '/' raise ConfigBugOrBrokenError, "called pullComment but // not seen" end double_slash = true end io = StringIO.new while true c = next_char_raw if (c == -1) || (c == "\n") put_back(c) if (double_slash) return Tokens.new_comment_double_slash(@line_origin, io.string) else return Tokens.new_comment_hash(@line_origin, io.string) end else io << c end end end # chars JSON allows a number to start with FIRST_NUMBER_CHARS = "0123456789-" # chars JSON allows to be part of a number NUMBER_CHARS = "0123456789eE+-." # chars that stop an unquoted string NOT_IN_UNQUOTED_TEXT = "$\"{}[]:=,+#`^?!@*&\\" # The rules here are intended to maximize convenience while # avoiding confusion with real valid JSON. Basically anything # that parses as JSON is treated the JSON way and otherwise # we assume it's a string and let the parser sort it out. def pull_unquoted_text origin = @line_origin io = StringIO.new c = next_char_raw while true if (c == -1) or (NOT_IN_UNQUOTED_TEXT.index(c)) or (self.class.whitespace?(c)) or (start_of_comment?(c)) break else io << c end # we parse true/false/null tokens as such no matter # what is after them, as long as they are at the # start of the unquoted token. if io.length == 4 if io.string == "true" return Tokens.new_boolean(origin, true) elsif io.string == "null" return Tokens.new_null(origin) end elsif io.length == 5 if io.string == "false" return Tokens.new_boolean(origin, false) end end c = next_char_raw end # put back the char that ended the unquoted text put_back(c) Tokens.new_unquoted_text(origin, io.string) end def pull_number(first_char) sb = StringIO.new sb << first_char contained_decimal_or_e = false c = next_char_raw while (c != -1) && (NUMBER_CHARS.index(c)) if (c == '.') || (c == 'e') || (c == 'E') contained_decimal_or_e = true end sb << c c = next_char_raw end # the last character we looked at wasn't part of the number, put it # back put_back(c) s = sb.string begin if contained_decimal_or_e # force floating point representation Tokens.new_double(@line_origin, Float(s), s) else Tokens.new_long(@line_origin, Integer(s), s) end rescue ArgumentError => e if e.message =~ /^invalid value for (Float|Integer)\(\)/ # not a number after all, see if it's an unquoted string. s.each_char do |u| if NOT_IN_UNQUOTED_TEXT.index(u) raise self.class.problem(@line_origin, u, "Reserved character '#{u}'" + "is not allowed outside quotes", true, nil) end end # no evil chars so we just decide this was a string and # not a number. Tokens.new_unquoted_text(@line_origin, s) else raise e end end end def pull_escape_sequence(sb, sb_orig) escaped = next_char_raw if escaped == -1 error_msg = "End of input but backslash in string had nothing after it" raise self.class.problem(@line_origin, "", error_msg, false, nil) end # This is needed so we return the unescaped escape characters back out when rendering # the token sb_orig << "\\" << escaped case escaped when "\"" sb << "\"" when "\\" sb << "\\" when "/" sb << "/" when "b" sb << "\b" when "f" sb << "\f" when "n" sb << "\n" when "r" sb << "\r" when "t" sb << "\t" when "u" codepoint = "" # Grab the 4 hex chars for the unicode character 4.times do c = next_char_raw if c == -1 error_msg = "End of input but expecting 4 hex digits for \\uXXXX escape" raise self.class.problem(@line_origin, c, error_msg, false, nil) end codepoint << c end sb_orig << codepoint # Convert codepoint to a unicode character packed = [codepoint.hex].pack("U") if packed == "_" raise self.class.problem(@line_origin, codepoint, "Malformed hex digits after \\u escape in string: '#{codepoint}'", false, nil) end sb << packed else error_msg = "backslash followed by '#{escaped}', this is not a valid escape sequence (quoted strings use JSON escaping, so use double-backslash \\ for literal backslash)" raise self.class.problem(Hocon::Impl::Tokenizer.as_string(escaped), "", error_msg, false, nil) end end def append_triple_quoted_string(sb, sb_orig) # we are after the opening triple quote and need to consume the # close triple consecutive_quotes = 0 while true c = next_char_raw if c == '"' consecutive_quotes += 1 elsif consecutive_quotes >= 3 # the last three quotes end the string and the other kept. sb.string = sb.string[0...-3] put_back c break else consecutive_quotes = 0 if c == -1 error_msg = "End of input but triple-quoted string was still open" raise self.class.problem(@line_origin, c, error_msg, false, nil) elsif c == "\n" # keep the line number accurate @line_number += 1 @line_origin = @origin.with_line_number(@line_number) end end sb << c sb_orig << c end end def pull_quoted_string # the open quote has already been consumed sb = StringIO.new # We need a second StringIO to keep track of escape characters. # We want to return them exactly as they appeared in the original text, # which means we will need a new StringIO to escape escape characters # so we can also keep the actual value of the string. This is gross. sb_orig = StringIO.new sb_orig << '"' c = "" while c != '"' c = next_char_raw if c == -1 raise self.class.problem(@line_origin, c, "End of input but string quote was still open", false, nil) end if c == "\\" pull_escape_sequence(sb, sb_orig) elsif c == '"' sb_orig << c # done! elsif c =~ /[[:cntrl:]]/ raise self.class.problem(@line_origin, c, "JSON does not allow unescaped #{c}" + " in quoted strings, use a backslash escape", false, nil) else sb << c sb_orig << c end end # maybe switch to triple-quoted string, sort of hacky... if sb.length == 0 third = next_char_raw if third == '"' sb_orig << third append_triple_quoted_string(sb, sb_orig) else put_back(third) end end Tokens.new_string(@line_origin, sb.string, sb_orig.string) end def pull_plus_equals # the initial '+' has already been consumed c = next_char_raw unless c == '=' error_msg = "'+' not followed by =, '#{c}' not allowed after '+'" raise self.class.problem(@line_origin, c, error_msg, true, nil) # true = suggest quotes end Tokens::PLUS_EQUALS end def pull_substitution # the initial '$' has already been consumed c = next_char_raw if c != '{' error_msg = "'$' not followed by {, '#{c}' not allowed after '$'" raise self.class.problem(@line_origin, c, error_msg, true, nil) # true = suggest quotes end optional = false c = next_char_raw if c == '?' optional = true else put_back(c) end saver = WhitespaceSaver.new expression = [] while true t = pull_next_token(saver) # note that we avoid validating the allowed tokens inside # the substitution here; we even allow nested substitutions # in the tokenizer. The parser sorts it out. if t == Tokens::CLOSE_CURLY # end the loop, done! break elsif t == Tokens::EOF raise self.class.problem(@line_origin, t, "Substitution ${ was not closed with a }", false, nil) else whitespace = saver.check(t, @line_origin, @line_number) unless whitespace.nil? expression << whitespace end expression << t end end Tokens.new_substitution(@line_origin, optional, expression) end def pull_next_token(saver) c = next_char_after_whitespace(saver) if c == -1 Tokens::EOF elsif c == "\n" # newline tokens have the just-ended line number line = Tokens.new_line(@line_origin) @line_number += 1 @line_origin = @origin.with_line_number(@line_number) line else t = nil if start_of_comment?(c) t = pull_comment(c) else t = case c when '"' then pull_quoted_string when '$' then pull_substitution when ':' then Tokens::COLON when ',' then Tokens::COMMA when '=' then Tokens::EQUALS when '{' then Tokens::OPEN_CURLY when '}' then Tokens::CLOSE_CURLY when '[' then Tokens::OPEN_SQUARE when ']' then Tokens::CLOSE_SQUARE when '+' then pull_plus_equals else nil end if t.nil? if FIRST_NUMBER_CHARS.index(c) t = pull_number(c) elsif NOT_IN_UNQUOTED_TEXT.index(c) raise self.class.problem(@line_origin, c, "Reserved character '#{c}' is not allowed outside quotes", true, nil) else put_back(c) t = pull_unquoted_text end end end if t.nil? raise ConfigBugOrBrokenError, "bug: failed to generate next token" end t end end def self.simple_value?(t) Tokens.substitution?(t) || Tokens.unquoted_text?(t) || Tokens.value?(t) end def queue_next_token t = pull_next_token(@whitespace_saver) whitespace = @whitespace_saver.check(t, @origin, @line_number) if whitespace @tokens.push(whitespace) end @tokens.push(t) end def has_next? !@tokens.empty? end def next t = @tokens.shift if (@tokens.empty?) and (t != Tokens::EOF) begin queue_next_token rescue TokenizerProblemError => e @tokens.push(e.problem) end if @tokens.empty? raise ConfigBugOrBrokenError, "bug: tokens queue should not be empty here" end end t end def remove raise ConfigBugOrBrokenError, "Does not make sense to remove items from token stream" end def each while has_next? # Have to use self.next instead of next because next is a reserved word yield self.next end end def map token_list = [] each do |token| # yield token to calling method, append whatever is returned from the # map block to token_list token_list << yield(token) end token_list end def to_list # Return array of tokens from the iterator self.map { |token| token } end end end hocon-1.3.1/lib/hocon/impl/config_node_array.rb0000644000004100000410000000034213704151031021456 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_node_complex_value' class Hocon::Impl::ConfigNodeArray include Hocon::Impl::ConfigNodeComplexValue def new_node(nodes) self.class.new(nodes) end endhocon-1.3.1/lib/hocon/impl/mergeable_value.rb0000644000004100000410000000022213704151031021122 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_mergeable' class Hocon::Impl::MergeableValue < Hocon::ConfigMergeable # TODO end hocon-1.3.1/lib/hocon/impl/memo_key.rb0000644000004100000410000000161113704151031017613 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl' class Hocon::Impl::MemoKey def initialize(value, restrict_to_child_or_nil) @value = value @restrict_to_child_or_nil = restrict_to_child_or_nil end def hash h = @value.hash if @restrict_to_child_or_nil != nil h + 41 * (41 + @restrict_to_child_or_nil.hash) else h end end def ==(other) if other.is_a?(self.class) o = other if !o.value.equal?(@value) return false elsif o.restrict_to_child_or_nil.equals(@restrict_to_child_or_nil) return true elsif o.restrict_to_child_or_nil == nil || @restrict_to_child_or_nil == nil return false else return o.restrict_to_child_or_nil == @restrict_to_child_or_nil end else false end end def to_s "MemoKey(#{@value}@#{@value.hash},#{@restrict_to_child_or_nil})" end end hocon-1.3.1/lib/hocon/impl/resolve_result.rb0000644000004100000410000000135413704151031021067 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl' # value is allowed to be null class Hocon::Impl::ResolveResult ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError attr_accessor :context, :value def initialize(context, value) @context = context @value = value end def self.make(context, value) self.new(context, value) end def as_object_result unless @value.is_a?(Hocon::Impl::AbstractConfigObject) raise ConfigBugOrBrokenError.new("Expecting a resolve result to be an object, but it was #{@value}") end self end def as_value_result self end def pop_trace self.class.make(@context.pop_trace, value) end def to_s "ResolveResult(#{@value})" end end hocon-1.3.1/lib/hocon/impl/config_impl.rb0000644000004100000410000002115513704151031020301 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/simple_includer' require 'hocon/config_error' require 'hocon/impl/from_map_mode' require 'hocon/impl/simple_config_origin' require 'hocon/impl/simple_config_list' require 'hocon/impl/config_boolean' require 'hocon/impl/config_null' require 'hocon/impl/parseable' class Hocon::Impl::ConfigImpl @default_includer = Hocon::Impl::SimpleIncluder.new(nil) @default_value_origin = Hocon::Impl::SimpleConfigOrigin.new_simple("hardcoded value") @default_true_value = Hocon::Impl::ConfigBoolean.new(@default_value_origin, true) @default_false_value = Hocon::Impl::ConfigBoolean.new(@default_value_origin, false) @default_null_value = Hocon::Impl::ConfigNull.new(@default_value_origin) @default_empty_list = Hocon::Impl::SimpleConfigList.new(@default_value_origin, Array.new) @default_empty_object = Hocon::Impl::SimpleConfigObject.empty(@default_value_origin) ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError FromMapMode = Hocon::Impl::FromMapMode def self.default_includer @default_includer end class FileNameSource < Hocon::Impl::SimpleIncluder::NameSource def name_to_parseable(name, parse_options) Hocon::Impl::Parseable.new_file(name, parse_options) end end def self.improve_not_resolved(what, original) new_message = "#{what.render} has not been resolved, you need to call Config#resolve, see API docs for Config#resolve" if new_message == original.message return original else return ConfigNotResolvedError.new(new_message, original) end end def self.value_origin(origin_description) if origin_description.nil? return @default_value_origin else return Hocon::Impl::SimpleConfigOrigin.new_simple(origin_description) end end def self.parse_file_any_syntax(basename, base_options) source = FileNameSource.new() Hocon::Impl::SimpleIncluder.from_basename(source, File.expand_path(basename), base_options) end def self.empty_list(origin) if origin.nil? || origin == @default_value_origin return @default_empty_list else return Hocon::Impl::SimpleConfigList.new(origin, Array.new) end end def self.from_any_ref(object, origin_description) origin = self.value_origin(origin_description) from_any_ref_mode(object, origin, FromMapMode::KEYS_ARE_KEYS) end def self.from_any_ref_mode(object, origin, map_mode) if origin.nil? raise ConfigBugOrBrokenError.new("origin not supposed to be nil") end if object.nil? if origin != @default_value_origin return Hocon::Impl::ConfigNull.new(origin) else return @default_null_value end elsif object.is_a?(Hocon::Impl::AbstractConfigValue) return object elsif object.is_a?(TrueClass) || object.is_a?(FalseClass) if origin != @default_value_origin return Hocon::Impl::ConfigBoolean.new(origin, object) elsif object return @default_true_value else return @default_false_value end elsif object.is_a?(String) return Hocon::Impl::ConfigString::Quoted.new(origin, object) elsif object.is_a?(Numeric) # here we always keep the same type that was passed to us, # rather than figuring out if a Long would fit in an Int # or a Double has no fractional part. i.e. deliberately # not using ConfigNumber.newNumber() when we have a # Double, Integer, or Long. if object.is_a?(Float) return Hocon::Impl::ConfigDouble.new(origin, object, nil) elsif object.is_a?(Integer) return Hocon::Impl::ConfigInt.new(origin, object, nil) else return Hocon::Impl::ConfigNumber.new_number(origin, Float(object), nil) end elsif object.is_a?(Hash) if object.empty? return self.empty_object_from_origin(origin) end if map_mode == FromMapMode::KEYS_ARE_KEYS values = Hash.new object.each do |key, entry| if not key.is_a?(String) raise ConfigBugOrBrokenError.new( "bug in method caller: not valid to create ConfigObject from map with non-String key: #{key}") end value = self.from_any_ref_mode(entry, origin, map_mode) values[key] = value end return Hocon::Impl::SimpleConfigObject.new(origin, values) else raise ConfigBugOrBrokenError, "java properties format not supported" end elsif object.is_a?(Enumerable) if object.count == 0 return self.empty_list(origin) end values = Array.new object.each do |item| v = from_any_ref_mode(item, origin, map_mode) values.push(v) end return Hocon::Impl::SimpleConfigList.new(origin, values) else raise ConfigBugOrBrokenError.new("bug in method caller: not valid to create ConfigValue from: #{object}") end end def self.env_variables_as_config_object EnvVariablesHolder.get_env_variables end # This class is a lot simpler than the Java version ... # The Java version uses system properties to toggle these settings. # We don't have system properties in MRI so it's not clear what to do here. # Initially, I ported this as more of a direct translation from the Java code, # but I ran into issues around how to translate stupid Java static # initialization crap to Ruby, so what we have here is a much simpler version # that is # equivalent. # # There's no way to toggle this logging without changing code, but it's # actually proved to be useful for debugging purposes while porting code # down from Java. class DebugHolder class << self def trace_loads_enabled TRACE_LOADS_ENABLED end def trace_substitutions_enabled TRACE_SUBSTITUTIONS_ENABLED end private TRACE_LOADS_ENABLED = false TRACE_SUBSTITUTIONS_ENABLED = false end end def self.trace_loads_enabled # Ignoring 'catch ExceptionInInitializerError' from that java version, # that is just terrible java code anyway. DebugHolder.trace_loads_enabled end def self.trace_substitution_enabled # Ignoring 'catch ExceptionInInitializerError' from that java version, # that is just terrible java code anyway. DebugHolder.trace_substitutions_enabled end def self.trace(message, indent_level = 0) while indent_level > 0 $stderr.putc(" ") indent_level -= 1 end $stderr.puts(message) end def self.empty_object_from_origin(origin) # we want null origin to go to SimpleConfigObject.empty() to get the # origin "empty config" rather than "hardcoded value" if origin == @default_value_origin @default_empty_object else Hocon::Impl::SimpleConfigObject.empty(origin) end end def self.empty_object(origin_description) if !origin_description.nil? origin = Hocon::Impl::SimpleConfigOrigin.new_simple(origin_description) else origin = nil end empty_object_from_origin(origin) end def self.empty_config(origin_description) empty_object(origin_description).to_config end def empty(origin) self.class.empty_object_from_origin(origin) end def self.default_reference resource = Hocon::Impl::Parseable.new_resources("reference.conf", Hocon::ConfigParseOptions.defaults) resource.parse.to_config end private def self.load_env_variables env = ENV m = {} lists = {} env.each do |key, value| m[key] = Hocon::Impl::ConfigString::Quoted.new( Hocon::Impl::SimpleConfigOrigin.new_simple("env var #{key}"), value) # find env var arrays specified like FOO.0, FOO.1 if key =~ /^(.*)\.(\d+)$/ name, index = $1, Integer($2) # values are added unordered (lists[name] ||= {})[index] = value end end lists.each do |key, values| origin = Hocon::Impl::SimpleConfigOrigin.new_simple("env var list #{key}") m[key] = Hocon::Impl::SimpleConfigList.new( origin, # out of order env vars FOO.1, FOO.0 are sorted here values.sort.map do |v| Hocon::Impl::ConfigString::Quoted.new(origin, v[1]) end ) end Hocon::Impl::SimpleConfigObject.new( Hocon::Impl::SimpleConfigOrigin.new_simple("env variables"), m, Hocon::Impl::ResolveStatus::RESOLVED, false) end class EnvVariablesHolder ENV_VARIABLES = Hocon::Impl::ConfigImpl.load_env_variables def self.get_env_variables ENV_VARIABLES end end end hocon-1.3.1/lib/hocon/impl/abstract_config_node.rb0000644000004100000410000000114013704151031022140 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/parser/config_node' require 'hocon/config_error' module Hocon::Impl::AbstractConfigNode include Hocon::Parser::ConfigNode def tokens raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of AbstractConfigNode should override `tokens` (#{self.class})" end def render orig_text = StringIO.new tokens.each do |t| orig_text << t.token_text end orig_text.string end def ==(other) other.is_a?(Hocon::Impl::AbstractConfigNode) && (render == other.render) end def hash render.hash end end hocon-1.3.1/lib/hocon/impl/config_boolean.rb0000644000004100000410000000074613704151031020762 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_value' class Hocon::Impl::ConfigBoolean include Hocon::Impl::AbstractConfigValue def initialize(origin, value) super(origin) @value = value end attr_reader :value def value_type Hocon::ConfigValueType::BOOLEAN end def unwrapped @value end def transform_to_string @value.to_s end def new_copy(origin) Hocon::Impl::ConfigBoolean.new(origin, @value) end end hocon-1.3.1/lib/hocon/impl/url.rb0000644000004100000410000000162313704151031016613 0ustar www-datawww-data# encoding: utf-8 require 'uri' require 'hocon/impl' # There are several places in the Java codebase that # use Java's URL constructor, and rely on it to throw # a `MalformedURLException` if the URL isn't valid. # # Ruby doesn't really have a similar constructor / # validator, so this is a little shim to hopefully # make the ported code match up with the upstream more # closely. class Hocon::Impl::Url class MalformedUrlError < StandardError def initialize(msg, cause = nil) super(msg) @cause = cause end end def initialize(url) begin # URI::parse wants a string @url = URI.parse(url.to_s) if !(@url.kind_of?(URI::HTTP)) raise MalformedUrlError, "Unrecognized URL: '#{url}'" end rescue URI::InvalidURIError => e raise MalformedUrlError.new("Unrecognized URL: '#{url}' (error: #{e})", e) end end def to_s @url.to_s end end hocon-1.3.1/lib/hocon/impl/config_number.rb0000644000004100000410000000320713704151031020626 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_value' class Hocon::Impl::ConfigNumber include Hocon::Impl::AbstractConfigValue ## sigh... requiring these subclasses before this class ## is declared would cause an error. Thanks, ruby. require 'hocon/impl/config_int' require 'hocon/impl/config_double' def self.new_number(origin, number, original_text) as_int = number.to_i if as_int == number Hocon::Impl::ConfigInt.new(origin, as_int, original_text) else Hocon::Impl::ConfigDouble.new(origin, number, original_text) end end def initialize(origin, original_text) super(origin) @original_text = original_text end attr_reader :original_text def transform_to_string @original_text end def int_value_range_checked(path) # We don't need to do any range checking here due to the way Ruby handles # integers (doesn't have the 32-bit/64-bit distinction that Java does). long_value end def long_value raise "long_value needs to be overriden by sub-classes of #{Hocon::Impl::ConfigNumber}, in this case #{self.class}" end def can_equal(other) other.is_a?(Hocon::Impl::ConfigNumber) end def ==(other) if other.is_a?(Hocon::Impl::ConfigNumber) && can_equal(other) @value == other.value else false end end def hash # This hash function makes it so that a ConfigNumber with a 3.0 # and one with a 3 will return the hash code to_int = @value.round # If the value is an integer or a floating point equal to an integer if to_int == @value to_int.hash else @value.hash end end end hocon-1.3.1/lib/hocon/impl/unmergeable.rb0000644000004100000410000000100613704151031020272 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_error' # # Interface that tags a ConfigValue that is not mergeable until after # substitutions are resolved. Basically these are special ConfigValue that # never appear in a resolved tree, like {@link ConfigSubstitution} and # {@link ConfigDelayedMerge}. # module Hocon::Impl::Unmergeable def unmerged_values raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Unmergeable` must implement `unmerged_values` (#{self.class})" end end hocon-1.3.1/lib/hocon/impl/path_parser.rb0000644000004100000410000002002213704151031020313 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'hocon/impl' require 'hocon/config_syntax' require 'hocon/impl/tokenizer' require 'hocon/impl/config_node_path' require 'hocon/impl/tokens' require 'hocon/config_value_type' require 'hocon/config_error' class Hocon::Impl::PathParser ConfigSyntax = Hocon::ConfigSyntax SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin Tokenizer = Hocon::Impl::Tokenizer Tokens = Hocon::Impl::Tokens ConfigNodePath = Hocon::Impl::ConfigNodePath ConfigValueType = Hocon::ConfigValueType ConfigBadPathError = Hocon::ConfigError::ConfigBadPathError class Element def initialize(initial, can_be_empty) @can_be_empty = can_be_empty @sb = StringIO.new(initial) end attr_accessor :can_be_empty, :sb def to_string "Element(#{@sb.string},#{@can_be_empty})" end end def self.api_origin SimpleConfigOrigin.new_simple("path parameter") end def self.parse_path_node(path, flavor = ConfigSyntax::CONF) reader = StringIO.new(path) begin tokens = Tokenizer.tokenize(api_origin, reader, flavor) tokens.next # drop START parse_path_node_expression(tokens, api_origin, path, flavor) ensure reader.close end end def self.parse_path(path) speculated = speculative_fast_parse_path(path) if not speculated.nil? return speculated end reader = StringIO.new(path) begin tokens = Tokenizer.tokenize(api_origin, reader, ConfigSyntax::CONF) tokens.next # drop START return parse_path_expression(tokens, api_origin, path) ensure reader.close end end def self.parse_path_node_expression(expression, origin, original_text = nil, flavor = ConfigSyntax::CONF) path_tokens = [] path = parse_path_expression(expression, origin, original_text, path_tokens, flavor) ConfigNodePath.new(path, path_tokens); end def self.parse_path_expression(expression, origin, original_text = nil, path_tokens = nil, flavor = ConfigSyntax::CONF) # each builder in "buf" is an element in the path buf = [] buf.push(Element.new("", false)) if !expression.has_next? raise ConfigBadPathError.new( origin, original_text, "Expecting a field name or path here, but got nothing") end while expression.has_next? t = expression.next if ! path_tokens.nil? path_tokens << t end # Ignore all IgnoredWhitespace tokens next if Tokens.ignored_whitespace?(t) if Tokens.value_with_type?(t, ConfigValueType::STRING) v = Tokens.value(t) # this is a quoted string; so any periods # in here don't count as path separators s = v.transform_to_string add_path_text(buf, true, s) elsif t == Tokens::EOF # ignore this; when parsing a file, it should not happen # since we're parsing a token list rather than the main # token iterator, and when parsing a path expression from the # API, it's expected to have an EOF. else # any periods outside of a quoted string count as # separators text = nil if Tokens.value?(t) # appending a number here may add # a period, but we _do_ count those as path # separators, because we basically want # "foo 3.0bar" to parse as a string even # though there's a number in it. The fact that # we tokenize non-string values is largely an # implementation detail. v = Tokens.value(t) # We need to split the tokens on a . so that we can get sub-paths but still preserve # the original path text when doing an insertion if ! path_tokens.nil? path_tokens.delete_at(path_tokens.size - 1) path_tokens.concat(split_token_on_period(t, flavor)) end text = v.transform_to_string elsif Tokens.unquoted_text?(t) # We need to split the tokens on a . so that we can get sub-paths but still preserve # the original path text when doing an insertion on ConfigNodeObjects if ! path_tokens.nil? path_tokens.delete_at(path_tokens.size - 1) path_tokens.concat(split_token_on_period(t, flavor)) end text = Tokens.unquoted_text(t) else raise ConfigBadPathError.new( origin, original_text, "Token not allowed in path expression: #{t}" + " (you can double-quote this token if you really want it here)") end add_path_text(buf, false, text) end end pb = Hocon::Impl::PathBuilder.new buf.each do |e| if (e.sb.length == 0) && !e.can_be_empty raise Hocon::ConfigError::ConfigBadPathError.new( origin, original_text, "path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)") else pb.append_key(e.sb.string) end end pb.result end def self.split_token_on_period(t, flavor) token_text = t.token_text if token_text == "." return [t] end split_token = token_text.split('.') split_tokens = [] split_token.each do |s| if flavor == ConfigSyntax::CONF split_tokens << Tokens.new_unquoted_text(t.origin, s) else split_tokens << Tokens.new_string(t.origin, s, "\"#{s}\"") end split_tokens << Tokens.new_unquoted_text(t.origin, ".") end if token_text[-1] != "." split_tokens.delete_at(split_tokens.size - 1) end split_tokens end def self.add_path_text(buf, was_quoted, new_text) i = if was_quoted -1 else new_text.index('.') || -1 end current = buf.last if i < 0 # add to current path element current.sb << new_text # any empty quoted string means this element can # now be empty. if was_quoted && (current.sb.length == 0) current.can_be_empty = true end else # "buf" plus up to the period is an element current.sb << new_text[0, i] # then start a new element buf.push(Element.new("", false)) # recurse to consume remainder of new_text add_path_text(buf, false, new_text[i + 1, new_text.length - 1]) end end # the idea is to see if the string has any chars or features # that might require the full parser to deal with. def self.looks_unsafe_for_fast_parser(s) last_was_dot = true # // start of path is also a "dot" len = s.length if s.empty? return true end if s[0] == "." return true end if s[len - 1] == "." return true end (0..len).each do |i| c = s[i] if c =~ /^\w$/ last_was_dot = false next elsif c == '.' if last_was_dot return true # ".." means we need to throw an error end last_was_dot = true elsif c == '-' if last_was_dot return true end next else return true end end if last_was_dot return true end false end def self.fast_path_build(tail, s, path_end) # rindex takes last index it should look at, end - 1 not end split_at = s.rindex(".", path_end - 1) tokens = [] tokens << Tokens.new_unquoted_text(nil, s) # this works even if split_at is -1; then we start the substring at 0 with_one_more_element = Path.new(s[split_at + 1..path_end], tail) if split_at < 0 with_one_more_element else fast_path_build(with_one_more_element, s, split_at) end end # do something much faster than the full parser if # we just have something like "foo" or "foo.bar" def self.speculative_fast_parse_path(path) s = Hocon::Impl::ConfigImplUtil.unicode_trim(path) if looks_unsafe_for_fast_parser(s) return nil end fast_path_build(nil, s, s.length) end end hocon-1.3.1/lib/hocon/impl/replaceable_merge_stack.rb0000644000004100000410000000126213704151031022613 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/container' require 'hocon/config_error' # # Implemented by a merge stack (ConfigDelayedMerge, ConfigDelayedMergeObject) # that replaces itself during substitution resolution in order to implement # "look backwards only" semantics. # module Hocon::Impl::ReplaceableMergeStack include Hocon::Impl::Container # # Make a replacement for this object skipping the given number of elements # which are lower in merge priority. # def make_replacement(context, skipping) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ReplaceableMergeStack` must implement `make_replacement` (#{self.class})" end end hocon-1.3.1/lib/hocon/impl/parseable.rb0000644000004100000410000004065313704151031017755 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'pathname' require 'hocon/impl' require 'hocon/config_error' require 'hocon/config_syntax' require 'hocon/config_value_type' require 'hocon/impl/config_impl' require 'hocon/impl/simple_include_context' require 'hocon/impl/simple_config_object' require 'hocon/impl/simple_config_origin' require 'hocon/impl/tokenizer' require 'hocon/impl/config_parser' require 'hocon/config_parseable' require 'hocon/impl/config_document_parser' require 'hocon/impl/simple_config_document' # # Internal implementation detail, not ABI stable, do not touch. # For use only by the {@link com.typesafe.config} package. # The point of this class is to avoid "propagating" each # overload on "thing which can be parsed" through multiple # interfaces. Most interfaces can have just one overload that # takes a Parseable. Also it's used as an abstract "resource # handle" in the ConfigIncluder interface. # class Hocon::Impl::Parseable include Hocon::ConfigParseable # Internal implementation detail, not ABI stable, do not touch module Relativizer def relative_to(filename) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Relativizer` must implement `relative_to` (#{self.class})" end end # The upstream library seems to use this as a global way of keeping track of # how many files have been included, to avoid cycles def self.parse_stack Thread.current[:hocon_parse_stack] ||= [] end MAX_INCLUDE_DEPTH = 50 def initialize end def fixup_options(base_options) syntax = base_options.syntax if !syntax syntax = guess_syntax end if !syntax syntax = Hocon::ConfigSyntax::CONF end modified = base_options.set_syntax(syntax) # make sure the app-provided includer falls back to default modified = modified.append_includer(Hocon::Impl::ConfigImpl.default_includer) # make sure the app-provided includer is complete modified = modified.set_includer(Hocon::Impl::SimpleIncluder.make_full(modified.includer)) modified end def post_construct(base_options) @initial_options = fixup_options(base_options) @include_context = Hocon::Impl::SimpleIncludeContext.new(self) if @initial_options.origin_description @initial_origin = Hocon::Impl::SimpleConfigOrigin.new_simple(@initial_options.origin_description) else @initial_origin = create_origin end end # the general idea is that any work should be in here, not in the # constructor, so that exceptions are thrown from the public parse() # function and not from the creation of the Parseable. # Essentially this is a lazy field. The parser should close the # reader when it's done with it. #{//}# ALSO, IMPORTANT: if the file or URL is not found, this must throw. #{//}# to support the "allow missing" feature. def custom_reader raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `custom_reader` (#{self.class})" end def reader(options) custom_reader end def self.trace(message) if Hocon::Impl::ConfigImpl.trace_loads_enabled Hocon::Impl::ConfigImpl.trace(message) end end def guess_syntax nil end def content_type nil end def relative_to(filename) # fall back to classpath; we treat the "filename" as absolute # (don't add a package name in front), # if it starts with "/" then remove the "/", for consistency # with ParseableResources.relativeTo resource = filename if filename.start_with?("/") resource = filename.slice(1) end self.class.new_resources(resource, options.set_origin_description(nil)) end def include_context @include_context end def self.force_parsed_to_object(value) if value.is_a? Hocon::Impl::AbstractConfigObject value else raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(value.origin, "", "object at file root", Hocon::ConfigValueType.value_type_name(value.value_type)) end end def parse(base_options = nil) if (base_options.nil?) base_options = options end stack = self.class.parse_stack if stack.length >= MAX_INCLUDE_DEPTH raise Hocon::ConfigError::ConfigParseError.new(@initial_origin, "include statements nested more than #{MAX_INCLUDE_DEPTH} times, " + "you probably have a cycle in your includes. Trace: #{stack}", nil) end # Push into beginning of stack stack.unshift(self) begin self.class.force_parsed_to_object(parse_value(base_options)) ensure # Pop from beginning of stack stack.shift end end def parse_value(base_options = nil) if base_options.nil? base_options = options end # note that we are NOT using our "initialOptions", # but using the ones from the passed-in options. The idea is that # callers can get our original options and then parse with different # ones if they want. options = fixup_options(base_options) # passed-in options can override origin origin = if options.origin_description Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description) else @initial_origin end parse_value_from_origin(origin, options) end def parse_value_from_origin(origin, final_options) begin raw_parse_value(origin, final_options) rescue IOError => e if final_options.allow_missing? Hocon::Impl::SimpleConfigObject.empty_missing(origin) else self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}") raise Hocon::ConfigError::ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e) end end end def parse_document(base_options = nil) if base_options.nil? base_options = options end # note that we are NOT using our "initialOptions", # but using the ones from the passed-in options. The idea is that # callers can get our original options and then parse with different # ones if they want. options = fixup_options(base_options) # passed-in option can override origin origin = nil if ! options.origin_description.nil? origin = Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description) else origin = @initial_origin end parse_document_from_origin(origin, options) end def parse_document_from_origin(origin, final_options) begin raw_parse_document(origin, final_options) rescue IOError => e if final_options.allow_missing? Hocon::Impl::SimpleConfigDocument.new( Hocon::Impl::ConfigNodeObject.new([]), final_options) else self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}") raise ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e) end end end # this is parseValue without post-processing the IOException or handling # options.getAllowMissing() def raw_parse_value(origin, final_options) reader = reader(final_options) # after reader() we will have loaded the Content-Type content_type = content_type() options_with_content_type = nil if !(content_type.nil?) if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?) self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}") end options_with_content_type = final_options.set_syntax(content_type) else options_with_content_type = final_options end reader.open { |io| raw_parse_value_from_io(io, origin, options_with_content_type) } end def raw_parse_value_from_io(io, origin, final_options) tokens = Hocon::Impl::Tokenizer.tokenize(origin, io, final_options.syntax) document = Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options) Hocon::Impl::ConfigParser.parse(document, origin, final_options, include_context) end def raw_parse_document(origin, final_options) reader = reader(final_options) content_type = content_type() options_with_content_type = nil if !(content_type.nil?) if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?) self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}") end options_with_content_type = final_options.set_syntax(content_type) else options_with_content_type = final_options end reader.open { |io| raw_parse_document_from_io(io, origin, options_with_content_type) } end def raw_parse_document_from_io(reader, origin, final_options) tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, final_options.syntax) Hocon::Impl::SimpleConfigDocument.new( Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options), final_options) end def parse_config_document parse_document(options) end def origin @initial_origin end def create_origin raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `create_origin` (#{self.class})" end def options @initial_options end def to_s self.class.name.split('::').last end def self.syntax_from_extension(name) if name.end_with?(".json") Hocon::ConfigSyntax::JSON elsif name.end_with?(".conf") Hocon::ConfigSyntax::CONF else # Skipping PROPERTIES because we can't really support that in ruby nil end end # NOTE: skipping `readerFromStream` and `doNotClose` because they don't seem relevant in Ruby # NOTE: skipping `relativeTo(URL, String)` because we're not supporting URLs for now def self.relative_to(file, filename) child = Pathname.new(filename) file = Pathname.new(file) if child.absolute? nil end parent = file.parent if parent.nil? nil else File.join(parent, filename) end end # this is a parseable that doesn't exist and just throws when you try to parse it class ParseableNotFound < Hocon::Impl::Parseable def initialize(what, message, options) super() @what = what @message = message post_construct(options) end def custom_reader raise Hocon::ConfigError::ConfigBugOrBrokenError, @message end def create_origin Hocon::Impl::SimpleConfigOrigin.new_simple(@what) end end def self.new_not_found(what_not_found, message, options) ParseableNotFound.new(what_not_found, message, options) end # NOTE: skipping `ParseableReader` until we know we need it (probably should # have done that with `ParseableNotFound`) class ParseableString < Hocon::Impl::Parseable def initialize(string, options) super() @input = string post_construct(options) end def custom_reader if Hocon::Impl::ConfigImpl.trace_loads_enabled self.class.trace("Loading config from a String: #{@input}") end # we return self here, which will cause `open` to be called on us, so # we can provide an implementation of that. self end def open if block_given? StringIO.open(@input) do |f| yield f end else StringIO.open(@input) end end def create_origin Hocon::Impl::SimpleConfigOrigin.new_simple("String") end def to_s "#{self.class.name.split('::').last} (#{@input})" end end def self.new_string(string, options) ParseableString.new(string, options) end # NOTE: Skipping `ParseableURL` for now as we probably won't support this right away class ParseableFile < Hocon::Impl::Parseable def initialize(input, options) super() @input = input post_construct(options) end def custom_reader if Hocon::Impl::ConfigImpl.trace_loads_enabled self.class.trace("Loading config from a String: #{@input}") end # we return self here, which will cause `open` to be called on us, so # we can provide an implementation of that. self end def open begin if block_given? File.open(@input, :encoding => 'bom|utf-8') do |f| yield f end else File.open(@input, :encoding => 'bom|utf-8') end rescue Errno::ENOENT if @initial_options.allow_missing? return Hocon::Impl::SimpleConfigObject.empty end raise Hocon::ConfigError::ConfigIOError.new(nil, "File not found. No file called #{@input}") end end def guess_syntax Hocon::Impl::Parseable.syntax_from_extension(File.basename(@input)) end def relative_to(filename) sibling = nil if Pathname.new(filename).absolute? sibling = File.new(filename) else # this may return nil sibling = Hocon::Impl::Parseable.relative_to(@input, filename) end if sibling.nil? nil elsif File.exists?(sibling) self.class.trace("#{sibling} exists, so loading it as a file") Hocon::Impl::Parseable.new_file(sibling, options.set_origin_description(nil)) else self.class.trace("#{sibling} does not exist, so trying it as a resource") super(filename) end end def create_origin Hocon::Impl::SimpleConfigOrigin.new_file(@input) end def to_s "#{self.class.name.split('::').last} (#{@input})" end end def self.new_file(file_path, options) ParseableFile.new(file_path, options) end # NOTE: skipping `ParseableResourceURL`, we probably won't support that # NOTE: this is not a faithful port of the `ParseableResources` class from the # upstream, because at least for now we're not going to try to do anything # crazy like look for files on the ruby load path. However, there is a decent # chunk of logic elsewhere in the codebase that is written with the assumption # that this class will provide the 'last resort' attempt to find a config file # before giving up, so we're basically port just enough to have it provide # that last resort behavior class ParseableResources < Hocon::Impl::Parseable include Relativizer def initialize(resource, options) super() @resource = resource post_construct(options) end def reader raise Hocon::ConfigError::ConfigBugOrBrokenError, "reader() should not be called on resources" end def raw_parse_value(origin, final_options) # this is where the upstream code would go out and look for a file on the # classpath. We're not going to do that, and instead we're just going to # raise the same exception that the upstream code would raise if it failed # to find the file. raise IOError, "resource not found: #{@resource}" end def guess_syntax Hocon::Impl::Parseable.syntax_from_extension(@resource) end def self.parent(resource) # the "resource" is not supposed to begin with a "/" # because it's supposed to be the raw resource # (ClassLoader#getResource), not the # resource "syntax" (Class#getResource) i = resource.rindex("/") if i < 0 nil else resource.slice(0..i) end end def relative_to(sibling) if sibling.start_with?("/") # if it starts with "/" then don't make it relative to the # including resource Hocon::Impl::Parseable.new_resources(sibling.slice(1), options.set_origin_description(nil)) else # here we want to build a new resource name and let # the class loader have it, rather than getting the # url with getResource() and relativizing to that url. # This is needed in case the class loader is going to # search a classpath. parent = self.class.parent(@resource) if parent.nil? Hocon::Impl::Parseable.new_resources(sibling, options.set_origin_description(nil)) else Hocon::Impl::Parseable.new_resources("#{parent}/sibling", options.set_origin_description(nil)) end end end def create_origin Hocon::Impl::SimpleConfigOrigin.new_resource(@resource) end def to_s "#{self.class.name.split('::').last}(#{@resource})" end end def self.new_resources(resource, options) ParseableResources.new(resource, options) end # NOTE: skipping `ParseableProperties`, we probably won't support that end hocon-1.3.1/lib/hocon/impl/config_string.rb0000644000004100000410000000363513704151031020651 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_value' require 'hocon/config_value_type' require 'hocon/impl/config_impl_util' class Hocon::Impl::ConfigString include Hocon::Impl::AbstractConfigValue ConfigImplUtil = Hocon::Impl::ConfigImplUtil attr_reader :value class Quoted < Hocon::Impl::ConfigString def initialize(origin, value) super(origin, value) end def new_copy(origin) self.class.new(origin, @value) end private # serialization all goes through SerializedConfigValue def write_replace Hocon::Impl::SerializedConfigValue.new(self) end end # this is sort of a hack; we want to preserve whether whitespace # was quoted until we process substitutions, so we can ignore # unquoted whitespace when concatenating lists or objects. # We dump this distinction when serializing and deserializing, # but that 's OK because it isn' t in equals/hashCode, and we # don 't allow serializing unresolved objects which is where # quoted-ness matters. If we later make ConfigOrigin point # to the original token range, we could use that to implement # wasQuoted() class Unquoted < Hocon::Impl::ConfigString def initialize(origin, value) super(origin, value) end def new_copy(origin) self.class.new(origin, @value) end def write_replace Hocon::Impl::SerializedConfigValue.new(self) end end def was_quoted? self.is_a?(Quoted) end def value_type Hocon::ConfigValueType::STRING end def unwrapped @value end def transform_to_string @value end def render_value_to_sb(sb, indent_size, at_root, options) if options.json? sb << ConfigImplUtil.render_json_string(@value) else sb << ConfigImplUtil.render_string_unquoted_if_possible(@value) end end private def initialize(origin, value) super(origin) @value = value end end hocon-1.3.1/lib/hocon/impl/resolve_status.rb0000644000004100000410000000051513704151031021072 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' class Hocon::Impl::ResolveStatus UNRESOLVED = 0 RESOLVED = 1 def self.from_values(values) if values.any? { |v| v.resolve_status == UNRESOLVED } UNRESOLVED else RESOLVED end end def self.from_boolean(resolved) resolved ? RESOLVED : UNRESOLVED end end hocon-1.3.1/lib/hocon/impl/container.rb0000644000004100000410000000202013704151031017763 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_value' require 'hocon/config_error' # An AbstractConfigValue which contains other values. Java has no way to # express "this has to be an AbstractConfigValue also" other than making # AbstractConfigValue an interface which would be aggravating. But we can say # we are a ConfigValue. module Hocon::Impl::Container include Hocon::ConfigValue # # Replace a child of this value. CAUTION if replacement is null, delete the # child, which may also delete the parent, or make the parent into a # non-container. # def replace_child(child, replacement) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `replace_child` (#{self.class})" end # # Super-expensive full traversal to see if descendant is anywhere # underneath this container. # def has_descendant?(descendant) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `has_descendant?` (#{self.class})" end end hocon-1.3.1/lib/hocon/impl/config_document_parser.rb0000644000004100000410000005410413704151031022532 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'hocon/impl' require 'hocon/config_error' require 'hocon/impl/tokens' require 'hocon/impl/config_node_single_token' require 'hocon/impl/config_node_comment' require 'hocon/impl/abstract_config_node_value' require 'hocon/impl/config_node_concatenation' require 'hocon/impl/config_include_kind' require 'hocon/impl/config_node_object' require 'hocon/impl/config_node_array' require 'hocon/impl/config_node_root' class Hocon::Impl::ConfigDocumentParser ConfigSyntax = Hocon::ConfigSyntax ConfigParseError = Hocon::ConfigError::ConfigParseError ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigValueType = Hocon::ConfigValueType Tokens = Hocon::Impl::Tokens PathParser = Hocon::Impl::PathParser ArrayIterator = Hocon::Impl::ArrayIterator ConfigImplUtil = Hocon::Impl::ConfigImplUtil ConfigIncludeKind = Hocon::Impl::ConfigIncludeKind ConfigNodeSingleToken = Hocon::Impl::ConfigNodeSingleToken ConfigNodeSimpleValue = Hocon::Impl::ConfigNodeSimpleValue ConfigNodeInclude = Hocon::Impl::ConfigNodeInclude ConfigNodeField = Hocon::Impl::ConfigNodeField ConfigNodeObject = Hocon::Impl::ConfigNodeObject ConfigNodeArray = Hocon::Impl::ConfigNodeArray ConfigNodeRoot = Hocon::Impl::ConfigNodeRoot def self.parse(tokens, origin, options) syntax = options.syntax.nil? ? ConfigSyntax::CONF : options.syntax context = Hocon::Impl::ConfigDocumentParser::ParseContext.new(syntax, origin, tokens) context.parse end def self.parse_value(tokens, origin, options) syntax = options.syntax.nil? ? ConfigSyntax::CONF : options.syntax context = Hocon::Impl::ConfigDocumentParser::ParseContext.new(syntax, origin, tokens) context.parse_single_value end class ParseContext def initialize(flavor, origin, tokens) @line_number = 1 @buffer = [] @tokens = tokens @flavor = flavor @equals_count = 0 @base_origin = origin end def pop_token if @buffer.empty? return @tokens.next end @buffer.pop end def next_token t = pop_token if @flavor.equal?(ConfigSyntax::JSON) if Tokens.unquoted_text?(t) && !unquoted_whitespace?(t) raise parse_error("Token not allowed in valid JSON: '#{Tokens.unquoted_text(t)}'") elsif Tokens.substitution?(t) raise parse_error("Substitutions (${} syntax) not allowed in JSON") end end t end def next_token_collecting_whitespace(nodes) while true t = next_token if Tokens.ignored_whitespace?(t) || Tokens.newline?(t) || unquoted_whitespace?(t) nodes.push(ConfigNodeSingleToken.new(t)) if Tokens.newline?(t) @line_number = t.line_number + 1 end elsif Tokens.comment?(t) nodes.push(Hocon::Impl::ConfigNodeComment.new(t)) else new_number = t.line_number if new_number >= 0 @line_number = new_number end return t end end end def put_back(token) @buffer.push(token) end # In arrays and objects, comma can be omitted # as long as there's at least one newline instead. # this skips any newlines in front of a comma, # skips the comma, and returns true if it found # either a newline or a comma. The iterator # is left just after the comma or the newline. def check_element_separator(nodes) if @flavor.equal?(ConfigSyntax::JSON) t = next_token_collecting_whitespace(nodes) if t.equal?(Tokens::COMMA) nodes.push(ConfigNodeSingleToken.new(t)) return true else put_back(t) return false end else saw_separator_or_new_line = false t = next_token while true if Tokens.ignored_whitespace?(t) || unquoted_whitespace?(t) nodes.push(ConfigNodeSingleToken.new(t)) elsif Tokens.comment?(t) nodes.push(Hocon::Impl::ConfigNodeComment.new(t)) elsif Tokens.newline?(t) saw_separator_or_new_line = true @line_number += 1 nodes.push(ConfigNodeSingleToken.new(t)) # we want to continue to also eat # a comma if there is one. elsif t.equal?(Tokens::COMMA) nodes.push(ConfigNodeSingleToken.new(t)) return true else # non-newline-or-comma put_back(t) return saw_separator_or_new_line end t = next_token end end end # parse a concatenation. If there is no concatenation, return the next value def consolidate_values(nodes) # this trick is not done in JSON if @flavor.equal?(ConfigSyntax::JSON) return nil end # create only if we have value tokens values = [] value_count = 0 # ignore a newline up front t = next_token_collecting_whitespace(nodes) while true v = nil if Tokens.ignored_whitespace?(t) values.push(ConfigNodeSingleToken.new(t)) t = next_token next elsif Tokens.value?(t) || Tokens.unquoted_text?(t) || Tokens.substitution?(t) || t == Tokens::OPEN_CURLY || t == Tokens::OPEN_SQUARE # there may be newlines _within_ the objects and arrays v = parse_value(t) value_count += 1 else break end if v.nil? raise ConfigBugOrBrokenError, "no value" end values.push(v) t = next_token # but don't consolidate across a newline end put_back(t) # No concatenation was seen, but a single value may have been parsed, so return it, and put back # all succeeding tokens if value_count < 2 value = nil values.each do |node| if node.is_a?(Hocon::Impl::AbstractConfigNodeValue) value = node elsif value.nil? nodes.add(node) else put_back(node.tokens[0]) end end return value end # Put back any trailing whitespace, as the parent object is responsible for tracking # any leading/trailing whitespace for i in (0..values.size - 1).reverse_each if values[i].is_a?(ConfigNodeSingleToken) put_back(values[i].token) values.delete_at(i) else break end end Hocon::Impl::ConfigNodeConcatenation.new(values) end def parse_error(message, cause = nil) ConfigParseError.new(@base_origin.with_line_number(@line_number), message, cause) end def add_quote_suggestion(bad_token, message, last_path = nil, inside_equals = nil) if inside_equals.nil? inside_equals = @equals_count > 0 end previous_field_name = last_path != nil ? last_path.render : nil if bad_token == Tokens::EOF.to_s # EOF requires special handling for the error to make sense. if previous_field_name != nil part = "#{message} (if you intended '#{previous_field_name}'" + "' to be part of a value, instead of a key, " + "try adding double quotes around the whole value" else return message end else if previous_field_name != nil part = "#{message} (if you intended #{bad_token}" + " to be part of the value for '#{previous_field_name}', " + "try enclosing the value in double quotes" else part = "#{message} (if you intended #{bad_token}" + " to be part of a key or string value, " + "try enclosing the key or value in double quotes" end end # Don't have a special case to throw a message about changing the file to .properties, since # we don't support that format part end def parse_value(t) v = nil starting_equals_count = @equals_count if Tokens.value?(t) || Tokens.unquoted_text?(t) || Tokens.substitution?(t) v = Hocon::Impl::ConfigNodeSimpleValue.new(t) elsif t.equal?(Tokens::OPEN_CURLY) v = parse_object(true) elsif t.equal?(Tokens::OPEN_SQUARE) v = parse_array else raise parse_error(add_quote_suggestion(t.to_s, "Expecting a value but got wrong token: #{t}")) end if @equals_count != starting_equals_count raise ConfigBugOrBrokenError, "Bug in config parser: unbalanced equals count" end v end def parse_key(token) if @flavor.equal?(ConfigSyntax::JSON) if Tokens.value_with_type?(token, ConfigValueType::STRING) return PathParser.parse_path_node_expression(Hocon::Impl::ArrayIterator.new([token]), nil) else raise ConfigParseError, "Expecting close brace } or a field name here, got #{token}" end else expression = [] t = token while Tokens.value?(t) || Tokens.unquoted_text?(t) expression.push(t) t = next_token # note: don't cross a newline end if expression.empty? raise parse_error("expecting a close brace or a field name here, got #{t}") end put_back(t) # put back the token we ended with PathParser.parse_path_node_expression(ArrayIterator.new(expression), nil) end end def include_keyword?(t) Tokens.unquoted_text?(t) && Tokens.unquoted_text(t) == "include" end def unquoted_whitespace?(t) unless Tokens.unquoted_text?(t) return false end s = Tokens.unquoted_text(t) s.each_char do |c| unless ConfigImplUtil.whitespace?(c) return false end end true end def key_value_separator?(t) if @flavor.equal?(ConfigSyntax::JSON) t.equal?(Tokens::COLON) else t.equal?(Tokens::COLON) || t.equal?(Tokens::EQUALS) || t.equal?(Tokens::PLUS_EQUALS) end end def parse_include(children) t = next_token_collecting_whitespace(children) # we either have a quoted string or the "file()" syntax if Tokens.unquoted_text?(t) # get foo( kind_text = Tokens.unquoted_text(t) if kind_text == "url(" kind = ConfigIncludeKind::URL elsif kind_text == "file(" kind = ConfigIncludeKind::FILE elsif kind_text == "classpath(" kind = ConfigIncludeKind::CLASSPATH else raise parse_error("expecting include parameter to be quoted filename, file(), classpath(), or url(). No spaces are allowed before the open paren. Not expecting: #{t}") end children.push(ConfigNodeSingleToken.new(t)) # skip space inside parens t = next_token_collecting_whitespace(children) # quoted string unless Tokens.value_with_type?(t, ConfigValueType::STRING) raise parse_error("expecting a quoted string inside file(), classpath(), or url(), rather than: #{t}") end children.push(ConfigNodeSimpleValue.new(t)) # skip space after string, inside parens t = next_token_collecting_whitespace(children) if Tokens.unquoted_text?(t) && Tokens.unquoted_text(t) == ")" # OK, close paren else raise parse_error("expecting a close parentheses ')' here, not: #{t}") end ConfigNodeInclude.new(children, kind) elsif Tokens.value_with_type?(t, ConfigValueType::STRING) children.push(ConfigNodeSimpleValue.new(t)) ConfigNodeInclude.new(children, ConfigIncludeKind::HEURISTIC) else raise parse_error("include keyword is not followed by a quoted string, but by: #{t}") end end def parse_object(had_open_curly) # invoked just after the OPEN_CURLY (or START, if !hadOpenCurly) after_comma = false last_path = nil last_inside_equals = false object_nodes = [] keys = Hash.new if had_open_curly object_nodes.push(ConfigNodeSingleToken.new(Tokens::OPEN_CURLY)) end while true t = next_token_collecting_whitespace(object_nodes) if t.equal?(Tokens::CLOSE_CURLY) if @flavor.equal?(ConfigSyntax::JSON) && after_comma raise parse_error(add_quote_suggestion(t.to_s, "expecting a field name after a comma, got a close brace } instead")) elsif !had_open_curly raise parse_error(add_quote_suggestion(t.to_s, "unbalanced close brace '}' with no open brace")) end object_nodes.push(ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY)) break elsif t.equal?(Tokens::EOF) && !had_open_curly put_back(t) break elsif !@flavor.equal?(ConfigSyntax::JSON) && include_keyword?(t) include_nodes = [] include_nodes.push(ConfigNodeSingleToken.new(t)) object_nodes.push(parse_include(include_nodes)) after_comma = false else key_value_nodes = [] key_token = t path = parse_key(key_token) key_value_nodes.push(path) after_key = next_token_collecting_whitespace(key_value_nodes) inside_equals = false if @flavor.equal?(ConfigSyntax::CONF) && after_key.equal?(Tokens::OPEN_CURLY) # can omit the ':' or '=' before an object value next_value = parse_value(after_key) else unless key_value_separator?(after_key) raise parse_error(add_quote_suggestion(after_key.to_s, "Key '#{path.render()}' may not be followed by token: #{after_key}")) end key_value_nodes.push(ConfigNodeSingleToken.new(after_key)) if after_key.equal?(Tokens::EQUALS) inside_equals = true @equals_count += 1 end next_value = consolidate_values(key_value_nodes) if next_value.nil? next_value = parse_value(next_token_collecting_whitespace(key_value_nodes)) end end key_value_nodes.push(next_value) if inside_equals @equals_count -= 1 end last_inside_equals = inside_equals key = path.value.first remaining = path.value.remainder if remaining.nil? existing = keys[key] unless existing.nil? # In strict JSON, dups should be an error; while in # our custom config language, they should be merged # if the value is an object (or substitution that # could become an object). if @flavor.equal?(ConfigSyntax::JSON) raise parse_error("JSON does not allow duplicate fields: '#{key}' was already seen") end end keys[key] = true else if @flavor.equal?(ConfigSyntax::JSON) raise ConfigBugOrBrokenError, "somehow got multi-element path in JSON mode" end keys[key] = true end after_comma = false object_nodes.push(ConfigNodeField.new(key_value_nodes)) end if check_element_separator(object_nodes) # continue looping after_comma = true else t = next_token_collecting_whitespace(object_nodes) if t.equal?(Tokens::CLOSE_CURLY) unless had_open_curly raise parse_error(add_quote_suggestion(t.to_s, "unbalanced close brace '}' with no open brace", last_path, last_inside_equals,)) end object_nodes.push(ConfigNodeSingleToken.new(t)) break elsif had_open_curly raise parse_error(add_quote_suggestion(t.to_s, "Expecting close brace } or a comma, got #{t}", last_path, last_inside_equals,)) else if t.equal?(Tokens::EOF) put_back(t) break else raise parse_error(add_quote_suggestion(t.to_s, "Expecting close brace } or a comma, got #{t}", last_path, last_inside_equals,)) end end end end ConfigNodeObject.new(object_nodes) end def parse_array children = [] children.push(ConfigNodeSingleToken.new(Tokens::OPEN_SQUARE)) # invoked just after the OPEN_SQUARE t = nil next_value = consolidate_values(children) unless next_value.nil? children.push(next_value) else t = next_token_collecting_whitespace(children) # special-case the first element if t.equal?(Tokens::CLOSE_SQUARE) children.push(ConfigNodeSingleToken.new(t)) return ConfigNodeArray.new(children) elsif Tokens.value?(t) || t.equal?(Tokens::OPEN_CURLY) || t.equal?(Tokens::OPEN_SQUARE) || Tokens.unquoted_text?(t) || Tokens.substitution?(t) next_value = parse_value(t) children.push(next_value) else raise parse_error("List should have ] or a first element after the open [, instead had token: #{t}" + " (if you want #{t} to be part of a string value, then double-quote it)") end end # now remaining elements while true # just after a value if check_element_separator(children) # comma (or newline equivalent) consumed else t = next_token_collecting_whitespace(children) if t.equal?(Tokens::CLOSE_SQUARE) children.push(ConfigNodeSingleToken.new(t)) return ConfigNodeArray.new(children) else raise parse_error("List should have ended with ] or had a comma, instead had token: #{t}" + " (if you want #{t} to be part of a string value, then double-quote it)") end end # now just after a comma next_value = consolidate_values(children) unless next_value.nil? children.push(next_value) else t = next_token_collecting_whitespace(children) if Tokens.value?(t) || t.equal?(Tokens::OPEN_CURLY) || t.equal?(Tokens::OPEN_SQUARE) || Tokens.unquoted_text?(t) || Tokens.substitution?(t) next_value = parse_value(t) children.push(next_value) elsif !@flavor.equal?(ConfigSyntax::JSON) && t.equal?(Tokens::CLOSE_SQUARE) # we allow one trailing comma put_back(t) else raise parse_error("List should have had new element after a comma, instead had token: #{t}" + " (if you want the comma or #{t} to be part of a string value, then double-quote it)") end end end end def parse children = [] t = next_token if t.equal?(Tokens::START) # OK else raise ConfigBugOrBrokenException, "token stream did not begin with START, had #{t}" end t = next_token_collecting_whitespace(children) result = nil missing_curly = false if t.equal?(Tokens::OPEN_CURLY) || t.equal?(Tokens::OPEN_SQUARE) result = parse_value(t) else if @flavor.equal?(ConfigSyntax::JSON) if t.equal?(Tokens::EOF) raise parse_error("Empty document") else raise parse_error("Document must have an object or array at root, unexpected token: #{t}") end else # the root object can omit the surrounding braces. # this token should be the first field's key, or part # of it, so put it back. put_back(t) missing_curly = true result = parse_object(false) end end # Need to pull the children out of the resulting node so we can keep leading # and trailing whitespace if this was a no-brace object. Otherwise, we need to add # the result into the list of children. if result.is_a?(ConfigNodeObject) && missing_curly children += result.children else children.push(result) end t = next_token_collecting_whitespace(children) if t.equal?(Tokens::EOF) if missing_curly # If there were no braces, the entire document should be treated as a single object ConfigNodeRoot.new([ConfigNodeObject.new(children)], @base_origin) else ConfigNodeRoot.new(children, @base_origin) end else raise parse_error("Document has trailing tokens after first object or array: #{t}") end end # Parse a given input stream into a single value node. Used when doing a replace inside a ConfigDocument. def parse_single_value t = next_token if t.equal?(Tokens::START) # OK else raise ConfigBugOrBrokenError, "token stream did not begin with START, had #{t}" end t = next_token if Tokens.ignored_whitespace?(t) || Tokens.newline?(t) || unquoted_whitespace?(t) || Tokens.comment?(t) raise parse_error("The value from setValue cannot have leading or trailing newlines, whitespace, or comments") end if t.equal?(Tokens::EOF) raise parse_error("Empty value") end if @flavor.equal?(ConfigSyntax::JSON) node = parse_value(t) t = next_token if t.equal?(Tokens::EOF) return node else raise parse_error("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments") end else put_back(t) nodes = [] node = consolidate_values(nodes) t = next_token if t.equal?(Tokens::EOF) node else raise parse_error("The value from setValue cannot have leading or trailing newlines, whitespace, or comments") end end end end endhocon-1.3.1/lib/hocon/impl/config_node_single_token.rb0000644000004100000410000000041113704151031023016 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_node' class Hocon::Impl::ConfigNodeSingleToken include Hocon::Impl::AbstractConfigNode def initialize(t) @token = t end attr_reader :token def tokens [@token] end endhocon-1.3.1/lib/hocon/impl/config_double.rb0000644000004100000410000000130113704151031020601 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_number' class Hocon::Impl::ConfigDouble < Hocon::Impl::ConfigNumber def initialize(origin, value, original_text) super(origin, original_text) @value = value end attr_reader :value def value_type Hocon::ConfigValueType::NUMBER end def unwrapped @value end def transform_to_string s = super if s.nil? @value.to_s else s end end def long_value @value.to_i end def double_value @value end def new_copy(origin) self.class.new(origin, @value, original_text) end # NOTE: skipping `writeReplace` from upstream, because it involves serialization end hocon-1.3.1/lib/hocon/impl/token.rb0000644000004100000410000000160713704151031017133 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/token_type' class Hocon::Impl::Token attr_reader :token_type, :token_text def self.new_without_origin(token_type, debug_string, token_text) Hocon::Impl::Token.new(token_type, nil, token_text, debug_string) end def initialize(token_type, origin, token_text = nil, debug_string = nil) @token_type = token_type @origin = origin @token_text = token_text @debug_string = debug_string end attr_reader :origin def line_number if @origin @origin.line_number else -1 end end def to_s if !@debug_string.nil? @debug_string else Hocon::Impl::TokenType.token_type_name(@token_type) end end def ==(other) # @origin deliberately left out other.is_a?(Hocon::Impl::Token) && @token_type == other.token_type end def hash @token_type.hash end end hocon-1.3.1/lib/hocon/impl/config_node_comment.rb0000644000004100000410000000100613704151031022000 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_error' require 'hocon/impl/config_node_single_token' require 'hocon/impl/tokens' class Hocon::Impl::ConfigNodeComment < Hocon::Impl::ConfigNodeSingleToken def initialize(comment) super(comment) unless Hocon::Impl::Tokens.comment?(@token) raise Hocon::ConfigError::ConfigBugOrBrokenError, 'Tried to create a ConfigNodeComment from a non-comment token' end end def comment_text Hocon::Impl::Tokens.comment_text(@token) end endhocon-1.3.1/lib/hocon/impl/abstract_config_object.rb0000644000004100000410000001434413704151031022473 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_value' require 'hocon/impl/simple_config' require 'hocon/config_object' require 'hocon/config_value_type' require 'hocon/impl/resolve_status' require 'hocon/impl/simple_config_origin' require 'hocon/config_error' require 'hocon/impl/config_impl' require 'hocon/impl/unsupported_operation_error' require 'hocon/impl/container' module Hocon::Impl::AbstractConfigObject include Hocon::ConfigObject include Hocon::Impl::Container include Hocon::Impl::AbstractConfigValue ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError def initialize(origin) super(origin) @config = Hocon::Impl::SimpleConfig.new(self) end def to_config @config end def to_fallback_value self end def with_only_key(key) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_key`" end def without_key(key) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_key`" end def with_value(key, value) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_value`" end def with_only_path_or_nil(path) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path_or_nil`" end def with_only_path(path) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path`" end def without_path(path) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_path`" end def with_path_value(path, value) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_path_value`" end # This looks up the key with no transformation or type conversion of any # kind, and returns null if the key is not present. The object must be # resolved along the nodes needed to get the key or # ConfigNotResolvedError will be thrown. # # @param key # @return the unmodified raw value or null def peek_assuming_resolved(key, original_path) begin attempt_peek_with_partial_resolve(key) rescue ConfigNotResolvedError => e raise Hocon::Impl::ConfigImpl.improve_not_resolved(original_path, e) end end # Look up the key on an only-partially-resolved object, with no # transformation or type conversion of any kind; if 'this' is not resolved # then try to look up the key anyway if possible. # # @param key # key to look up # @return the value of the key, or null if known not to exist # @throws ConfigNotResolvedError # if can't figure out key's value (or existence) without more # resolving def attempt_peek_with_partial_resolve(key) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `attempt_peek_with_partial_resolve`" end # Looks up the path with no transformation or type conversion. Returns null # if the path is not found; throws ConfigException.NotResolved if we need # to go through an unresolved node to look up the path. def peek_path(path) peek_path_from_obj(self, path) end def peek_path_from_obj(obj, path) begin # we'll fail if anything along the path can't be looked at without resolving path_next = path.remainder v = obj.attempt_peek_with_partial_resolve(path.first) if path_next.nil? v else if v.is_a?(Hocon::Impl::AbstractConfigObject) peek_path_from_obj(v, path_next) else nil end end rescue ConfigNotResolvedError => e raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e) end end def value_type Hocon::ConfigValueType::OBJECT end def new_copy_with_status(status, origin) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `new_copy_with_status`" end def new_copy(origin) new_copy_with_status(resolve_status, origin) end def construct_delayed_merge(origin, stack) Hocon::Impl::ConfigDelayedMergeObject.new(origin, stack) end def merged_with_object(fallback) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `merged_with_object`" end def with_fallback(mergeable) super(mergeable) end def self.merge_origins(stack) if stack.empty? raise ConfigBugOrBrokenError, "can't merge origins on empty list" end origins = [] first_origin = nil num_merged = 0 stack.each do |v| if first_origin.nil? first_origin = v.origin end if (v.is_a?(Hocon::Impl::AbstractConfigObject)) && (v.resolve_status == Hocon::Impl::ResolveStatus::RESOLVED) && v.empty? # don't include empty files or the .empty() # config in the description, since they are # likely to be "implementation details" else origins.push(v.origin) num_merged += 1 end end if num_merged == 0 # the configs were all empty, so just use the first one origins.push(first_origin) end Hocon::Impl::SimpleConfigOrigin.merge_origins(origins) end def resolve_substitutions(context, source) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `resolve_substituions`" end def relativized(path) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `relativized`" end def [](key) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `[]`" end def render_value_to_sb(sb, indent, at_root, options) raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `render_value_to_sb`" end def we_are_immutable(method) Hocon::Impl::UnsupportedOperationError.new("ConfigObject is immutable, you can't call Map.#{method}") end def clear raise we_are_immutable("clear") end def []=(key, value) raise we_are_immutable("[]=") end def putAll(map) raise we_are_immutable("putAll") end def remove(key) raise we_are_immutable("remove") end def delete(key) raise we_are_immutable("delete") end def with_origin(origin) super(origin) end end hocon-1.3.1/lib/hocon/impl/config_delayed_merge_object.rb0000644000004100000410000002060513704151031023453 0ustar www-datawww-datarequire 'hocon/impl' require 'hocon/impl/unmergeable' require 'hocon/impl/replaceable_merge_stack' # This is just like ConfigDelayedMerge except we know statically # that it will turn out to be an object. class Hocon::Impl::ConfigDelayedMergeObject include Hocon::Impl::Unmergeable include Hocon::Impl::ReplaceableMergeStack include Hocon::Impl::AbstractConfigObject def initialize(origin, stack) super(origin) @stack = stack if stack.empty? raise Hocon::ConfigError::ConfigBugOrBrokenError.new("creating empty delayed merge value", nil) end if !@stack[0].is_a? Hocon::Impl::AbstractConfigObject error_message = "created a delayed merge object not guaranteed to be an object" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end stack.each do |v| if v.is_a?(Hocon::Impl::ConfigDelayedMergeObject) || v.is_a?(Hocon::Impl::ConfigDelayedMergeObject) error_message = "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end end end attr_reader :stack def new_copy(status, origin) if status != resolve_status raise Hocon::ConfigError::ConfigBugOrBrokenError.new( "attempt to create resolved ConfigDelayedMergeObject") end Hocon::Impl::ConfigDelayedMergeObject.new(origin, @stack) end def resolve_substitutions(context, source) merged = Hocon::Impl::ConfigDelayedMerge.resolve_substitutions(self, @stack, context, source) merged.as_object_result end def make_replacement(context, skipping) Hocon::Impl::ConfigDelayedMerge.make_replacement(context, @stack, skipping) end def resolve_status Hocon::Impl::ResolveStatus::UNRESOLVED end def replace_child(child, replacement) new_stack = Hocon::Impl::AbstractConfigValue.replace_child_in_list(@stack, child, replacement) if new_stack == nil nil else self.class.new(origin, new_stack) end end def has_descendant?(descendant) Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(@stack, descendant) end def relativized(prefix) new_stack = [] @stack.each { |o| new_stack << o.relativized(prefix) } self.class.new(origin, new_stack) end def ignores_fallbacks? Hocon::Impl::ConfigDelayedMerge.stack_ignores_fallbacks?(@stack) end def merged_with_the_unmergeable(fallback) require_not_ignoring_fallbacks merged_stack_with_the_unmergeable(@stack, fallback) end def merged_with_object(fallback) merged_with_non_object(fallback) end def merged_with_non_object(fallback) require_not_ignoring_fallbacks merged_stack_with_non_object(@stack, fallback) end # No implementation of withFallback here, # just use the implementation in the super-class def with_only_key(key) raise self.class.not_resolved end def without_key(key) raise self.class.not_resolved end def with_only_path_or_nil(key) raise self.class.not_resolved end def with_only_path(key) raise self.class.not_resolved end def without_path(key) raise self.class.not_resolved end def with_value(key_or_path, value = nil) raise self.class.not_resolved end def unmerged_values @stack end def can_equal(other) other.is_a? Hocon::Impl::ConfigDelayedMergeObject end def ==(other) # note that "origin" is deliberately NOT part of equality if other.is_a? Hocon::Impl::ConfigDelayedMergeObject can_equal(other) && (@stack == other.stack || @stack.equal?(other.stack)) else false end end def hash # note that "origin" is deliberately NOT part of equality @stack.hash end def render_to_sb(sb, indent, at_root, at_key, options) Hocon::Impl::ConfigDelayedMerge.render_value_to_sb_from_stack(@stack, sb, indent, at_root, at_key, options) end def self.not_resolved error_message = "need to Config#resolve() before using this object, see the API docs for Config#resolve()" Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil) end def unwrapped raise self.class.not_resolved end def [](key) raise self.class.not_resolved end def has_key?(key) raise self.class.not_resolved end def has_value?(value) raise self.class.not_resolved end def each raise self.class.not_resolved end def empty? raise self.class.not_resolved end def keys raise self.class.not_resolved end def values raise self.class.not_resolved end def size raise self.class.not_resolved end def self.unmergeable?(object) # Ruby note: This is the best way I could find to simulate # else if (layer instanceof Unmergeable) in java since we're including # the Unmergeable module instead of extending an Unmergeable class object.class.included_modules.include?(Hocon::Impl::Unmergeable) end def attempt_peek_with_partial_resolve(key) # a partial resolve of a ConfigDelayedMergeObject always results in a # SimpleConfigObject because all the substitutions in the stack get # resolved in order to look up the partial. # So we know here that we have not been resolved at all even # partially. # Given that, all this code is probably gratuitous, since the app code # is likely broken. But in general we only throw NotResolved if you try # to touch the exact key that isn't resolved, so this is in that # spirit. # we'll be able to return a key if we have a value that ignores # fallbacks, prior to any unmergeable values. @stack.each do |layer| if layer.is_a?(Hocon::Impl::AbstractConfigObject) v = layer.attempt_peek_with_partial_resolve(key) if !v.nil? if v.ignores_fallbacks? # we know we won't need to merge anything in to this # value return v else # we can't return this value because we know there are # unmergeable values later in the stack that may # contain values that need to be merged with this # value. we'll throw the exception when we get to those # unmergeable values, so continue here. next end elsif self.class.unmergeable?(layer) error_message = "should not be reached: unmergeable object returned null value" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) else # a non-unmergeable AbstractConfigObject that returned null # for the key in question is not relevant, we can keep # looking for a value. next end elsif self.class.unmergeable?(layer) error_message = "Key '#{key}' is not available at '#{origin.description}'" + "because value at '#{layer.origin.description}' has not been resolved" + " and may turn out to contain or hide '#{key}'. Be sure to Config#resolve()" + " before using a config object" raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil) elsif layer.resolved_status == ResolveStatus::UNRESOLVED # if the layer is not an object, and not a substitution or # merge, # then it's something that's unresolved because it _contains_ # an unresolved object... i.e. it's an array if !layer.is_a?(Hocon::Impl::ConfigList) error_message = "Expecting a list here, not #{layer}" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end return nil else # non-object, but resolved, like an integer or something. # has no children so the one we're after won't be in it. # we would only have this in the stack in case something # else "looks back" to it due to a cycle. # anyway at this point we know we can't find the key anymore. if !layer.ignores_fallbacks? error_message = "resolved non-object should ignore fallbacks" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end return nil end end # If we get here, then we never found anything unresolved which means # the ConfigDelayedMergeObject should not have existed. some # invariant was violated. error_message = "Delayed merge stack does not contain any unmergeable values" raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil) end end hocon-1.3.1/lib/hocon/impl/config_node_field.rb0000644000004100000410000000357413704151031021435 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_error' require 'hocon/impl/abstract_config_node' require 'hocon/impl/abstract_config_node_value' require 'hocon/impl/config_node_comment' require 'hocon/impl/config_node_path' require 'hocon/impl/config_node_single_token' require 'hocon/impl/tokens' class Hocon::Impl::ConfigNodeField include Hocon::Impl::AbstractConfigNode Tokens = Hocon::Impl::Tokens def initialize(children) @children = children end attr_reader :children def tokens tokens = [] @children.each do |child| tokens += child.tokens end tokens end def replace_value(new_value) children_copy = @children.clone children_copy.each_with_index do |child, i| if child.is_a?(Hocon::Impl::AbstractConfigNodeValue) children_copy[i] = new_value return self.class.new(children_copy) end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value" end def value @children.each do |child| if child.is_a?(Hocon::Impl::AbstractConfigNodeValue) return child end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value" end def path @children.each do |child| if child.is_a?(Hocon::Impl::ConfigNodePath) return child end end raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a path" end def separator @children.each do |child| if child.is_a?(Hocon::Impl::ConfigNodeSingleToken) t = child.token if t == Tokens::PLUS_EQUALS or t == Tokens::COLON or t == Tokens::EQUALS return t end end end nil end def comments comments = [] @children.each do |child| if child.is_a?(Hocon::Impl::ConfigNodeComment) comments << child.comment_text end end comments end endhocon-1.3.1/lib/hocon/impl/resolve_context.rb0000644000004100000410000002021013704151031021225 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' require 'hocon/impl/resolve_source' require 'hocon/impl/resolve_memos' require 'hocon/impl/memo_key' require 'hocon/impl/abstract_config_value' require 'hocon/impl/config_impl' class Hocon::Impl::ResolveContext ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve attr_reader :restrict_to_child def initialize(memos, options, restrict_to_child, resolve_stack, cycle_markers) @memos = memos @options = options @restrict_to_child = restrict_to_child @resolve_stack = resolve_stack @cycle_markers = cycle_markers end def self.new_cycle_markers # This looks crazy, but wtf else should we do with # return Collections.newSetFromMap(new IdentityHashMap()); Set.new end def add_cycle_marker(value) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("++ Cycle marker #{value}@#{value.hash}", depth) end if @cycle_markers.include?(value) raise ConfigBugOrBrokenError.new("Added cycle marker twice " + value) end copy = self.class.new_cycle_markers copy.merge(@cycle_markers) copy.add(value) self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy) end def remove_cycle_marker(value) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("-- Cycle marker #{value}@#{value.hash}", depth) end copy = self.class.new_cycle_markers copy.merge(@cycle_markers) copy.delete(value) self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy) end def memoize(key, value) changed = @memos.put(key, value) self.class.new(changed, @options, @restrict_to_child, @resolve_stack, @cycle_markers) end def options @options end def is_restricted_to_child @restrict_to_child != nil end def restrict(restrict_to) if restrict_to.equal?(@restrict_to_child) self else Hocon::Impl::ResolveContext.new(@memos, @options, restrict_to, @resolve_stack, @cycle_markers) end end def unrestricted restrict(nil) end def resolve(original, source) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "resolving #{original} restrict_to_child=#{@restrict_to_child} in #{source}", depth) end push_trace(original).real_resolve(original, source).pop_trace end def real_resolve(original, source) # a fully-resolved (no restrict_to_child) object can satisfy a # request for a restricted object, so always check that first. full_key = Hocon::Impl::MemoKey.new(original, nil) restricted_key = nil cached = @memos.get(full_key) # but if there was no fully-resolved object cached, we'll only # compute the restrictToChild object so use a more limited # memo key if cached == nil && is_restricted_to_child restricted_key = Hocon::Impl::MemoKey.new(original, @restrict_to_child) cached = @memos.get(restricted_key) end if cached != nil if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "using cached resolution #{cached} for #{original} restrict_to_child #{@restrict_to_child}", depth) end Hocon::Impl::ResolveResult.make(self, cached) else if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "not found in cache, resolving #{original}@#{original.hash}", depth) end if @cycle_markers.include?(original) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "Cycle detected, can't resolve; #{original}@#{original.hash}", depth) end raise NotPossibleToResolve.new(self) end result = original.resolve_substitutions(self, source) resolved = result.value if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "resolved to #{resolved}@#{resolved.hash} from #{original}@#{resolved.hash}", depth) end with_memo = result.context if resolved == nil || resolved.resolve_status == Hocon::Impl::ResolveStatus::RESOLVED # if the resolved object is fully resolved by resolving # only the restrictToChildOrNull, then it can be cached # under fullKey since the child we were restricted to # turned out to be the only unresolved thing. if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "caching #{full_key} result #{resolved}", depth) end with_memo = with_memo.memoize(full_key, resolved) else # if we have an unresolved object then either we did a # partial resolve restricted to a certain child, or we are # allowing incomplete resolution, or it's a bug. if is_restricted_to_child if restricted_key == nil raise ConfigBugOrBrokenError.new("restricted_key should not be null here") end if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "caching #{restricted_key} result #{resolved}", depth) end with_memo = with_memo.memoize(restricted_key, resolved) elsif @options.allow_unresolved if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "caching #{full_key} result #{resolved}", depth) end with_memo = with_memo.memoize(full_key, resolved) else raise ConfigBugOrBrokenError.new( "resolve_substitutions did not give us a resolved object") end end Hocon::Impl::ResolveResult.make(with_memo, resolved) end end # This method is a translation of the constructor in the Java version with signature # ResolveContext(ConfigResolveOptions options, Path restrictToChild) def self.construct(options, restrict_to_child) context = self.new(Hocon::Impl::ResolveMemos.new, options, restrict_to_child, [], new_cycle_markers) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace( "ResolveContext restrict to child #{restrict_to_child}", context.depth) end context end def trace_string separator = ", " sb = "" @resolve_stack.each { |value| if value.instance_of?(Hocon::Impl::ConfigReference) sb << value.expression.to_s sb << separator end } if sb.length > 0 sb.chomp!(separator) end sb end def depth if @resolve_stack.size > 30 raise Hocon::ConfigError::ConfigBugOrBrokenError.new("resolve getting too deep") end @resolve_stack.size end def self.resolve(value, root, options) source = Hocon::Impl::ResolveSource.new(root) context = construct(options, nil) begin context.resolve(value, source).value rescue NotPossibleToResolve => e # ConfigReference was supposed to catch NotPossibleToResolve raise ConfigBugOrBrokenError( "NotPossibleToResolve was thrown from an outermost resolve", e) end end def pop_trace copy = @resolve_stack.clone old = copy.delete_at(@resolve_stack.size - 1) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("popped trace #{old}", depth - 1) end Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers) end private def push_trace(value) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("pushing trace #{value}", depth) end copy = @resolve_stack.clone copy << value Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers) end end hocon-1.3.1/lib/hocon/impl/config_include_kind.rb0000644000004100000410000000021013704151031021755 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' module Hocon::Impl::ConfigIncludeKind URL = 0 FILE = 1 CLASSPATH = 2 HEURISTIC = 3 end hocon-1.3.1/lib/hocon/impl/config_node_simple_value.rb0000644000004100000410000000243013704151031023025 0ustar www-datawww-data# encoding: utf-8 require 'hocon/config_error' require 'hocon/impl' require 'hocon/impl/abstract_config_node_value' require 'hocon/impl/array_iterator' require 'hocon/impl/config_reference' require 'hocon/impl/config_string' require 'hocon/impl/path_parser' require 'hocon/impl/substitution_expression' require 'hocon/impl/tokens' class Hocon::Impl::ConfigNodeSimpleValue include Hocon::Impl::AbstractConfigNodeValue Tokens = Hocon::Impl::Tokens def initialize(value) @token = value end attr_reader :token def tokens [@token] end def value if Tokens.value?(@token) return Tokens.value(@token) elsif Tokens.unquoted_text?(@token) return Hocon::Impl::ConfigString::Unquoted.new(@token.origin, Tokens.unquoted_text(@token)) elsif Tokens.substitution?(@token) expression = Tokens.get_substitution_path_expression(@token) path = Hocon::Impl::PathParser.parse_path_expression(Hocon::Impl::ArrayIterator.new(expression), @token.origin) optional = Tokens.get_substitution_optional(@token) return Hocon::Impl::ConfigReference.new(@token.origin, Hocon::Impl::SubstitutionExpression.new(path, optional)) end raise Hocon::ConfigError::ConfigBugOrBrokenError, 'ConfigNodeSimpleValue did not contain a valid value token' end endhocon-1.3.1/lib/hocon/impl/config_null.rb0000644000004100000410000000072713704151031020314 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_value_type' class Hocon::Impl::ConfigNull include Hocon::Impl::AbstractConfigValue def initialize(origin) super(origin) end def value_type Hocon::ConfigValueType::NULL end def unwrapped nil end def transform_to_string "null" end def render_value_to_sb(sb, indent, at_root, options) sb << "null" end def new_copy(origin) self.class.new(origin) end end hocon-1.3.1/lib/hocon/impl/config_node_concatenation.rb0000644000004100000410000000035213704151031023166 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_node_complex_value' class Hocon::Impl::ConfigNodeConcatenation include Hocon::Impl::ConfigNodeComplexValue def new_node(nodes) self.class.new(nodes) end endhocon-1.3.1/lib/hocon/impl/simple_config.rb0000644000004100000410000002213513704151031020630 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_value_type' require 'hocon/config_resolve_options' require 'hocon/impl/path' require 'hocon/impl/default_transformer' require 'hocon/impl/config_impl' require 'hocon/impl/resolve_context' require 'hocon/config_mergeable' class Hocon::Impl::SimpleConfig include Hocon::ConfigMergeable ConfigMissingError = Hocon::ConfigError::ConfigMissingError ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError ConfigNullError = Hocon::ConfigError::ConfigNullError ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError ConfigValueType = Hocon::ConfigValueType Path = Hocon::Impl::Path DefaultTransformer = Hocon::Impl::DefaultTransformer attr_reader :object def initialize(object) @object = object end attr_reader :object def root @object end def origin @object.origin end def resolve(options = Hocon::ConfigResolveOptions.defaults) resolve_with(self, options) end def resolve_with(source, options) resolved = Hocon::Impl::ResolveContext.resolve(@object, source.object, options) if resolved.eql?(@object) self else Hocon::Impl::SimpleConfig.new(resolved) end end def self.find_key(me, key, expected, original_path) v = me.peek_assuming_resolved(key, original_path) if v.nil? raise ConfigMissingError.new(nil, "No configuration setting found for key '#{original_path.render}'", nil) end if not expected.nil? v = DefaultTransformer.transform(v, expected) end if v.value_type == ConfigValueType::NULL raise ConfigNullError.new(v.origin, (ConfigNullError.make_message(original_path.render, (not expected.nil?) ? ConfigValueType.value_type_name(expected) : nil)), nil) elsif (not expected.nil?) && v.value_type != expected raise ConfigWrongTypeError.new(v.origin, "#{original_path.render} has type #{ConfigValueType.value_type_name(v.value_type)} " + "rather than #{ConfigValueType.value_type_name(expected)}", nil) else return v end end def find(me, path, expected, original_path) key = path.first rest = path.remainder if rest.nil? self.class.find_key(me, key, expected, original_path) else o = self.class.find_key(me, key, ConfigValueType::OBJECT, original_path.sub_path(0, original_path.length - rest.length)) raise "Error: object o is nil" unless not o.nil? find(o, rest, expected, original_path) end end def find3(path_expression, expected, original_path) find(@object, path_expression, expected, original_path) end def find2(path_expression, expected) path = Path.new_path(path_expression) find3(path, expected, path) end def ==(other) if other.is_a? Hocon::Impl::SimpleConfig @object == other.object else false end end def hash 41 * @object.hash end def self.find_key_or_null(me, key, expected, original_path) v = me.peek_assuming_resolved(key, original_path) if v.nil? raise Hocon::ConfigError::ConfigMissingError.new(nil, original_path.render, nil) end if not expected.nil? v = Hocon::Impl::DefaultTransformer.transform(v, expected) end if (not expected.nil?) && (v.value_type != expected && v.value_type != ConfigValueType::NULL) raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(v.origin, original_path.render, ConfigValueType.value_type_name(expected), ConfigValueType.value_type_name(v.value_type)) else return v end end def self.find_or_null(me, path, expected, original_path) begin key = path.first remainder = path.remainder if remainder.nil? return self.find_key_or_null(me, key, expected, original_path) else o = find_key(me, key, ConfigValueType::OBJECT, original_path.sub_path(0, original_path.length - remainder.length)) if o.nil? raise "Missing key: #{key} on path: #{path}" end find_or_null(o, remainder, expected, original_path) end rescue Hocon::ConfigError::ConfigNotResolvedError raise Hocon::Impl::ConfigImpl::improved_not_resolved(path, e) end end def is_null?(path_expression) path = Path.new_path(path_expression) v = self.class.find_or_null(@object, path, nil, path) v.value_type == ConfigValueType::NULL end def get_value(path) parsed_path = Path.new_path(path) find(@object, parsed_path, nil, parsed_path) end def get_boolean(path) v = find2(path, ConfigValueType::BOOLEAN) v.unwrapped end def get_config_number(path_expression) path = Path.new_path(path_expression) v = find(@object, path, ConfigValueType::NUMBER, path) v.unwrapped end def get_int(path) get_config_number(path) end def get_string(path) v = find2(path, ConfigValueType::STRING) v.unwrapped end def get_list(path) find2(path, ConfigValueType::LIST) end def get_object(path) find2(path, ConfigValueType::OBJECT) end def get_config(path) get_object(path).to_config end def get_any_ref(path) v = find2(path, nil) v.unwrapped end def get_bytes(path) size = null begin size = get_long(path) rescue ConfigWrongTypeError => e v = find2(path, ConfigValueType::STRING) size = self.class.parse_bytes(v.unwrapped, v.origin, path) end size end def get_homogeneous_unwrapped_list(path, expected) l = [] list = get_list(path) list.each do |cv| if !expected.nil? v = DefaultTransformer.transform(cv, expected) end if v.value_type != expected raise ConfigWrongTypeError.with_expected_actual(origin, path, "list of #{ConfigValueType.value_type_name(expected)}", "list of #{ConfigValueType.value_type_name(v.value_type)}") end l << v.unwrapped end l end def get_boolean_list(path) get_homogeneous_unwrapped_list(path, ConfigValueType::BOOLEAN) end def get_number_list(path) get_homogeneous_unwrapped_list(path, ConfigValueType::NUMBER) end def get_int_list(path) l = [] numbers = get_homogeneous_wrapped_list(path, ConfigValueType::NUMBER) numbers.each do |v| l << v.int_value_range_checked(path) end l end def get_double_list(path) l = [] numbers = get_number_list(path) numbers.each do |n| l << n.double_value end l end def get_string_list(path) get_homogeneous_unwrapped_list(path, ConfigValueType::STRING) end def get_object_list(path) get_homogeneous_wrapped_list(path, ConfigValueType::OBJECT) end def get_homogeneous_wrapped_list(path, expected) l = [] list = get_list(path) list.each do |cv| if !expected.nil? v = DefaultTransformer.transform(cv, expected) end if v.value_type != expected raise ConfigWrongTypeError.with_expected_actual(origin, path, "list of #{ConfigValueType.value_type_name(expected)}", "list of #{ConfigValueType.value_type_name(v.value_type)}") end l << v end l end def has_path_peek(path_expression) path = Path.new_path(path_expression) begin peeked = @object.peek_path(path) rescue Hocon::ConfigError::ConfigNotResolvedError raise Hocon::Impl::ConfigImpl.improved_not_resolved(path, e) end peeked end def has_path?(path_expression) peeked = has_path_peek(path_expression) (not peeked.nil?) && peeked.value_type != ConfigValueType::NULL end def has_path_or_null?(path) peeked = has_path_peek(path) not peeked.nil? end def empty? @object.empty? end def at_key(key) root.at_key(key) end # In java this is an overloaded version of atKey def at_key_with_origin(origin, key) root.at_key_with_origin(origin, key) end def with_only_path(path_expression) path = Path.new_path(path_expression) self.class.new(root.with_only_path(path)) end def without_path(path_expression) path = Path.new_path(path_expression) self.class.new(root.without_path(path)) end def with_value(path_expression, v) path = Path.new_path(path_expression) self.class.new(root.with_value(path, v)) end def to_fallback_value @object end def with_fallback(other) @object.with_fallback(other).to_config end end hocon-1.3.1/lib/hocon/impl/from_map_mode.rb0000644000004100000410000000073513704151031020620 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/config_error' module Hocon::Impl::FromMapMode KEYS_ARE_PATHS = 0 KEYS_ARE_KEYS = 1 ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError def self.map_mode_name(from_map_mode) case from_map_mode when KEYS_ARE_PATHS then "KEYS_ARE_PATHS" when KEYS_ARE_KEYS then "KEYS_ARE_KEYS" else raise ConfigBugOrBrokenError.new("Unrecognized FromMapMode #{from_map_mode}") end end endhocon-1.3.1/lib/hocon/impl/full_includer.rb0000644000004100000410000000011413704151031020632 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' class Hocon::Impl::FullIncluder endhocon-1.3.1/lib/hocon/impl/config_int.rb0000644000004100000410000000122213704151031020123 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_number' require 'hocon/config_value_type' class Hocon::Impl::ConfigInt < Hocon::Impl::ConfigNumber def initialize(origin, value, original_text) super(origin, original_text) @value = value end attr_reader :value def value_type Hocon::ConfigValueType::NUMBER end def unwrapped @value end def transform_to_string s = super if s.nil? self.to_s else s end end def long_value @value end def double_value @value end def new_copy(origin) Hocon::Impl::ConfigInt.new(origin, @value, @original_text) end end hocon-1.3.1/lib/hocon/impl/config_node_complex_value.rb0000644000004100000410000000315313704151031023206 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_node_value' require 'hocon/impl/config_node_field' require 'hocon/impl/config_node_include' require 'hocon/impl/config_node_single_token' require 'hocon/impl/tokens' require 'hocon/config_error' module Hocon::Impl::ConfigNodeComplexValue include Hocon::Impl::AbstractConfigNodeValue def initialize(children) @children = children end attr_reader :children def tokens tokens = [] @children.each do |child| tokens += child.tokens end tokens end def indent_text(indentation) children_copy = @children.clone i = 0 while i < children_copy.size child = children_copy[i] if child.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Hocon::Impl::Tokens.newline?(child.token) children_copy.insert(i + 1, indentation) i += 1 elsif child.is_a?(Hocon::Impl::ConfigNodeField) value = child.value if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) children_copy[i] = child.replace_value(value.indent_text(indentation)) end elsif child.is_a?(Hocon::Impl::ConfigNodeComplexValue) children_copy[i] = child.indent_text(indentation) end i += 1 end new_node(children_copy) end # This method will just call into the object's constructor, but it's needed # for use in the indentText() method so we can avoid a gross if/else statement # checking the type of this def new_node(nodes) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigNodeComplexValue should override `new_node` (#{self.class})" end endhocon-1.3.1/lib/hocon/impl/config_node_object.rb0000644000004100000410000002526413704151031021620 0ustar www-datawww-data# encoding: utf-8 require 'hocon/config_syntax' require 'hocon/impl' require 'hocon/impl/config_node_complex_value' require 'hocon/impl/config_node_field' require 'hocon/impl/config_node_single_token' require 'hocon/impl/tokens' class Hocon::Impl::ConfigNodeObject include Hocon::Impl::ConfigNodeComplexValue ConfigSyntax = Hocon::ConfigSyntax Tokens = Hocon::Impl::Tokens def new_node(nodes) self.class.new(nodes) end def has_value(desired_path) @children.each do |node| if node.is_a?(Hocon::Impl::ConfigNodeField) field = node key = field.path.value if key == desired_path || key.starts_with(desired_path) return true elsif desired_path.starts_with(key) if field.value.is_a?(self.class) obj = field.value remaining_path = desired_path.sub_path_to_end(key.length) if obj.has_value(remaining_path) return true end end end end end false end def change_value_on_path(desired_path, value, flavor) children_copy = @children.clone seen_non_matching = false # Copy the value so we can change it to null but not modify the original parameter value_copy = value i = children_copy.size while i >= 0 do if children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) t = children_copy[i].token # Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma if flavor.equal?(ConfigSyntax::JSON) && !seen_non_matching && t.equal?(Tokens::COMMA) children_copy.delete_at(i) end i -= 1 next elsif !children_copy[i].is_a?(Hocon::Impl::ConfigNodeField) i -= 1 next end node = children_copy[i] key = node.path.value # Delete all multi-element paths that start with the desired path, since technically they are duplicates if (value_copy.nil? && key == desired_path) || (key.starts_with(desired_path) && !(key == desired_path)) children_copy.delete_at(i) # Remove any whitespace or commas after the deleted setting j = i while j < children_copy.size if children_copy[j].is_a?(Hocon::Impl::ConfigNodeSingleToken) t = children_copy[j].token if Tokens.ignored_whitespace?(t) || t.equal?(Tokens::COMMA) children_copy.delete_at(j) j -= 1 else break end else break end j += 1 end elsif key == desired_path seen_non_matching = true before = i - 1 > 0 ? children_copy[i - 1] : nil if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && before.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(before.token) indented_value = value.indent_text(before) else indented_value = value end children_copy[i] = node.replace_value(indented_value) value_copy = nil elsif desired_path.starts_with(key) seen_non_matching = true if node.value.is_a?(self.class) remaining_path = desired_path.sub_path_to_end(key.length) children_copy[i] = node.replace_value(node.value.change_value_on_path(remaining_path, value_copy, flavor)) if !value_copy.nil? && !(node == @children[i]) value_copy = nil end end else seen_non_matching = true end i -= 1 end self.class.new(children_copy) end def set_value_on_path(desired_path, value, flavor = ConfigSyntax::CONF) path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor) set_value_on_path_node(path, value, flavor) end def set_value_on_path_node(desired_path, value, flavor) node = change_value_on_path(desired_path.value, value, flavor) # If the desired Path did not exist, add it unless node.has_value(desired_path.value) return node.add_value_on_path(desired_path, value, flavor) end node end def indentation seen_new_line = false indentation = [] if @children.empty? return indentation end @children.each_index do |i| unless seen_new_line if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(@children[i].token) seen_new_line = true indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil))) end else if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(@children[i].token) && i + 1 < @children.size && (@children[i + 1].is_a?(Hocon::Impl::ConfigNodeField) || @children[i + 1].is_a?(Hocon::Impl::ConfigNodeInclude)) # Return the indentation of the first setting on its own line indentation.push(@children[i]) return indentation end end end if indentation.empty? indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, " "))) return indentation else # Calculate the indentation of the ending curly-brace to get the indentation of the root object last = @children[-1] if last.is_a?(Hocon::Impl::ConfigNodeSingleToken) && last.token.equal?(Tokens::CLOSE_CURLY) beforeLast = @children[-2] indent = "" if beforeLast.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(beforeLast.token) indent = beforeLast.token.token_text end indent += " " indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, indent))) return indentation end end # The object has no curly braces and is at the root level, so don't indent indentation end def add_value_on_path(desired_path, value, flavor) path = desired_path.value children_copy = @children.clone indentation = indentation().clone # If the value we're inserting is a complex value, we'll need to indent it for insertion if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && indentation.length > 0 indented_value = value.indent_text(indentation[-1]) else indented_value = value end same_line = !(indentation.length > 0 && indentation[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(indentation[0].token)) # If the path is of length greater than one, see if the value needs to be added further down if path.length > 1 (0..@children.size - 1).reverse_each do |i| unless @children[i].is_a?(Hocon::Impl::ConfigNodeField) next end node = @children[i] key = node.path.value if path.starts_with(key) && node.value.is_a?(self.class) remaining_path = desired_path.sub_path(key.length) new_value = node.value children_copy[i] = node.replace_value(new_value.add_value_on_path(remaining_path, value, flavor)) return self.class.new(children_copy) end end end # Otherwise, construct the new setting starts_with_brace = @children[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && @children[0].token.equal?(Tokens::OPEN_CURLY) new_nodes = [] new_nodes += indentation new_nodes.push(desired_path.first) new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COLON)) new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' '))) if path.length == 1 new_nodes.push(indented_value) else # If the path is of length greater than one add the required new objects along the path new_object_nodes = [] new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::OPEN_CURLY)) if indentation.empty? new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil))) end new_object_nodes += indentation new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY)) new_object = self.class.new(new_object_nodes) new_nodes.push(new_object.add_value_on_path(desired_path.sub_path(1), indented_value, flavor)) end # Combine these two cases so that we only have to iterate once if flavor.equal?(ConfigSyntax::JSON) || starts_with_brace || same_line i = children_copy.size - 1 while i >= 0 # If we are in JSON or are adding a setting on the same line, we need to add a comma to the # last setting if (flavor.equal?(ConfigSyntax::JSON) || same_line) && children_copy[i].is_a?(Hocon::Impl::ConfigNodeField) if i + 1 >= children_copy.size || !(children_copy[i + 1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i + 1].token.equal?(Tokens::COMMA)) children_copy.insert(i + 1, Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COMMA)) break end end # Add the value into the copy of the children map, keeping any whitespace/newlines # before the close curly brace if starts_with_brace && children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i].token == Tokens::CLOSE_CURLY previous = children_copy[i - 1] if previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(previous.token) children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes)) i -= 1 elsif previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(previous.token) before_previous = children_copy[i - 2] if same_line children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes)) i -= 1 elsif before_previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(before_previous.token) children_copy.insert(i - 2, Hocon::Impl::ConfigNodeField.new(new_nodes)) i -= 2 else children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes)) end else children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes)) end end i -= 1 end end unless starts_with_brace if children_copy[-1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(children_copy[-1].token) children_copy.insert(-2, Hocon::Impl::ConfigNodeField.new(new_nodes)) else children_copy.push(Hocon::Impl::ConfigNodeField.new(new_nodes)) end end self.class.new(children_copy) end def remove_value_on_path(desired_path, flavor) path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor).value change_value_on_path(path, nil, flavor) end end hocon-1.3.1/lib/hocon/impl/config_parser.rb0000644000004100000410000003263513704151031020641 0ustar www-datawww-data# encoding: utf-8 require 'stringio' require 'hocon/impl' require 'hocon/impl/path_builder' require 'hocon/config_syntax' require 'hocon/impl/config_string' require 'hocon/impl/config_concatenation' require 'hocon/config_error' require 'hocon/impl/simple_config_list' require 'hocon/impl/simple_config_object' require 'hocon/impl/path' require 'hocon/impl/url' require 'hocon/impl/config_reference' require 'hocon/impl/substitution_expression' require 'hocon/impl/config_node_simple_value' require 'hocon/impl/config_node_object' require 'hocon/impl/config_node_array' require 'hocon/impl/config_node_concatenation' require 'hocon/impl/config_include_kind' class Hocon::Impl::ConfigParser ConfigSyntax = Hocon::ConfigSyntax ConfigConcatenation = Hocon::Impl::ConfigConcatenation ConfigReference = Hocon::Impl::ConfigReference ConfigParseError = Hocon::ConfigError::ConfigParseError ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError SimpleConfigObject = Hocon::Impl::SimpleConfigObject SimpleConfigList = Hocon::Impl::SimpleConfigList Path = Hocon::Impl::Path ConfigIncludeKind = Hocon::Impl::ConfigIncludeKind ConfigNodeInclude = Hocon::Impl::ConfigNodeInclude ConfigNodeComment = Hocon::Impl::ConfigNodeComment ConfigNodeSingleToken = Hocon::Impl::ConfigNodeSingleToken Tokens = Hocon::Impl::Tokens def self.parse(document, origin, options, include_context) context = Hocon::Impl::ConfigParser::ParseContext.new( options.syntax, origin, document, Hocon::Impl::SimpleIncluder.make_full(options.includer), include_context) context.parse end class ParseContext def initialize(flavor, origin, document, includer, include_context) @line_number = 1 @document = document @flavor = flavor @base_origin = origin @includer = includer @include_context = include_context @path_stack = [] @array_count = 0 end # merge a bunch of adjacent values into one # value; change unquoted text into a string # value. def parse_concatenation(n) # this trick is not done in JSON if @flavor.equal?(ConfigSyntax::JSON) raise ConfigBugOrBrokenError, "Found a concatenation node in JSON" end values = [] n.children.each do |node| if node.is_a?(Hocon::Impl::AbstractConfigNodeValue) v = parse_value(node, nil) values.push(v) end end ConfigConcatenation.concatenate(values) end def line_origin @base_origin.with_line_number(@line_number) end def parse_error(message, cause = nil) ConfigParseError.new(line_origin, message, cause) end def full_current_path # pathStack has top of stack at front if @path_stack.empty? raise ConfigBugOrBrokenError, "Bug in parser; tried to get current path when at root" else Path.from_path_list(@path_stack.reverse) end end def parse_value(n, comments) starting_array_count = @array_count if n.is_a?(Hocon::Impl::ConfigNodeSimpleValue) v = n.value elsif n.is_a?(Hocon::Impl::ConfigNodeObject) v = parse_object(n) elsif n.is_a?(Hocon::Impl::ConfigNodeArray) v = parse_array(n) elsif n.is_a?(Hocon::Impl::ConfigNodeConcatenation) v = parse_concatenation(n) else raise parse_error("Expecting a value but got wrong node type: #{n.class}") end unless comments.nil? || comments.empty? v = v.with_origin(v.origin.prepend_comments(comments.clone)) comments.clear end unless @array_count == starting_array_count raise ConfigBugOrBrokenError, "Bug in config parser: unbalanced array count" end v end def create_value_under_path(path, value) # for path foo.bar, we are creating # { "foo" : { "bar" : value } } keys = [] key = path.first remaining = path.remainder until key.nil? keys.push(key) if remaining.nil? break else key = remaining.first remaining = remaining.remainder end end # the setComments(null) is to ensure comments are only # on the exact leaf node they apply to. # a comment before "foo.bar" applies to the full setting # "foo.bar" not also to "foo" keys = keys.reverse # this is just a ruby means for doing first/rest deepest, *rest = *keys o = SimpleConfigObject.new(value.origin.with_comments(nil), {deepest => value}) while !rest.empty? deepest, *rest = *rest o = SimpleConfigObject.new(value.origin.with_comments(nil), {deepest => o}) end o end def parse_include(values, n) case n.kind when ConfigIncludeKind::URL url = nil begin url = Hocon::Impl::Url.new(n.name) rescue Hocon::Impl::Url::MalformedUrlError => e raise parse_error("include url() specifies an invalid URL: #{n.name}", e) end obj = @includer.include_url(@include_context, url) when ConfigIncludeKind::FILE obj = @includer.include_file(@include_context, n.name) when ConfigIncludeKind::CLASSPATH obj = @includer.include_resources(@include_context, n.name) when ConfigIncludeKind::HEURISTIC obj = @includer.include(@include_context, n.name) else raise ConfigBugOrBrokenError, "should not be reached" end # we really should make this work, but for now throwing an # exception is better than producing an incorrect result. # See https://github.com/typesafehub/config/issues/160 if @array_count > 0 && (obj.resolve_status != Hocon::Impl::ResolveStatus::RESOLVED) raise parse_error("Due to current limitations of the config parser, when an include statement is nested inside a list value, " + "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or " + "remove the ${} statements from the included file.") end if !(@path_stack.empty?) prefix = full_current_path obj = obj.relativized(prefix) end obj.key_set.each do |key| v = obj.get(key) existing = values[key] if !(existing.nil?) values[key] = v.with_fallback(existing) else values[key] = v end end end def parse_object(n) values = Hash.new object_origin = line_origin last_was_new_line = false nodes = n.children.clone comments = [] i = 0 while i < nodes.size node = nodes[i] if node.is_a?(ConfigNodeComment) last_was_new_line = false comments.push(node.comment_text) elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token) @line_number += 1 if last_was_new_line # Drop all comments if there was a blank line and start a new comment block comments.clear end last_was_new_line = true elsif !@flavor.equal?(ConfigSyntax::JSON) && node.is_a?(ConfigNodeInclude) parse_include(values, node) last_was_new_line = false elsif node.is_a?(Hocon::Impl::ConfigNodeField) last_was_new_line = false path = node.path.value comments += node.comments # path must be on-stack while we parse the value # Note that, in upstream, pathStack is a LinkedList, so use unshift instead of push @path_stack.unshift(path) if node.separator.equal?(Tokens::PLUS_EQUALS) # we really should make this work, but for now throwing # an exception is better than producing an incorrect # result. See # https://github.com/typesafehub/config/issues/160 if @array_count > 0 raise parse_error("Due to current limitations of the config parser, += does not work nested inside a list. " + "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. " + "You might be able to move the += outside of the list and then refer to it from inside the list with ${}.") end # because we will put it in an array after the fact so # we want this to be incremented during the parseValue # below in order to throw the above exception. @array_count += 1 end value_node = node.value # comments from the key token go to the value token new_value = parse_value(value_node, comments) if node.separator.equal?(Tokens::PLUS_EQUALS) @array_count -= 1 concat = [] previous_ref = ConfigReference.new(new_value.origin, Hocon::Impl::SubstitutionExpression.new(full_current_path, true)) list = SimpleConfigList.new(new_value.origin, [new_value]) concat << previous_ref concat << list new_value = ConfigConcatenation.concatenate(concat) end # Grab any trailing comments on the same line if i < nodes.size - 1 i += 1 while i < nodes.size if nodes[i].is_a?(ConfigNodeComment) comment = nodes[i] new_value = new_value.with_origin(new_value.origin.append_comments([comment.comment_text])) break elsif nodes[i].is_a?(ConfigNodeSingleToken) curr = nodes[i] if curr.token.equal?(Tokens::COMMA) || Tokens.ignored_whitespace?(curr.token) # keep searching, as there could still be a comment else i -= 1 break end else i -= 1 break end i += 1 end end @path_stack.shift key = path.first remaining = path.remainder if remaining.nil? existing = values[key] unless existing.nil? # In strict JSON, dups should be an error; while in # our custom config language, they should be merged # if the value is an object (or substitution that # could become an object). if @flavor.equal?(ConfigSyntax::JSON) raise parse_error("JSON does not allow duplicate fields: '#{key}'" + " was already seen at #{existing.origin().description()}") else new_value = new_value.with_fallback(existing) end end values[key] = new_value else if @flavor == ConfigSyntax::JSON raise Hocon::ConfigError::ConfigBugOrBrokenError, "somehow got multi-element path in JSON mode" end obj = create_value_under_path(remaining, new_value) existing = values[key] if !existing.nil? obj = obj.with_fallback(existing) end values[key] = obj end end i += 1 end SimpleConfigObject.new(object_origin, values) end def parse_array(n) @array_count += 1 array_origin = line_origin values = [] last_was_new_line = false comments = [] v = nil n.children.each do |node| if node.is_a?(ConfigNodeComment) comments << node.comment_text last_was_new_line = false elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token) @line_number += 1 if last_was_new_line && v.nil? comments.clear elsif !v.nil? values << v.with_origin(v.origin.append_comments(comments.clone)) comments.clear v = nil end last_was_new_line = true elsif node.is_a?(Hocon::Impl::AbstractConfigNodeValue) last_was_new_line = false unless v.nil? values << v.with_origin(v.origin.append_comments(comments.clone)) comments.clear end v = parse_value(node, comments) end end # There shouldn't be any comments at this point, but add them just in case unless v.nil? values << v.with_origin(v.origin.append_comments(comments.clone)) end @array_count -= 1 SimpleConfigList.new(array_origin, values) end def parse result = nil comments = [] last_was_new_line = false @document.children.each do |node| if node.is_a?(ConfigNodeComment) comments << node.comment_text last_was_new_line = false elsif node.is_a?(ConfigNodeSingleToken) t = node.token if Tokens.newline?(t) @line_number += 1 if last_was_new_line && result.nil? comments.clear elsif !result.nil? result = result.with_origin(result.origin.append_comments(comments.clone)) comments.clear break end last_was_new_line = true end elsif node.is_a?(Hocon::Impl::ConfigNodeComplexValue) result = parse_value(node, comments) last_was_new_line = false end end result end end end hocon-1.3.1/lib/hocon/impl/token_type.rb0000644000004100000410000000216313704151031020172 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' class Hocon::Impl::TokenType START = 0 EOF = 1 COMMA = 2 EQUALS = 3 COLON = 4 OPEN_CURLY = 5 CLOSE_CURLY = 6 OPEN_SQUARE = 7 CLOSE_SQUARE = 8 VALUE = 9 NEWLINE = 10 UNQUOTED_TEXT = 11 SUBSTITUTION = 12 PROBLEM = 13 COMMENT = 14 PLUS_EQUALS = 15 IGNORED_WHITESPACE = 16 def self.token_type_name(token_type) case token_type when START then "START" when EOF then "EOF" when COMMA then "COMMA" when EQUALS then "EQUALS" when COLON then "COLON" when OPEN_CURLY then "OPEN_CURLY" when CLOSE_CURLY then "CLOSE_CURLY" when OPEN_SQUARE then "OPEN_SQUARE" when CLOSE_SQUARE then "CLOSE_SQUARE" when VALUE then "VALUE" when NEWLINE then "NEWLINE" when UNQUOTED_TEXT then "UNQUOTED_TEXT" when SUBSTITUTION then "SUBSTITUTION" when PROBLEM then "PROBLEM" when COMMENT then "COMMENT" when PLUS_EQUALS then "PLUS_EQUALS" when IGNORED_WHITESPACE then "IGNORED_WHITESPACE" else raise ConfigBugOrBrokenError, "Unrecognized token type #{token_type}" end end end hocon-1.3.1/lib/hocon/impl/config_concatenation.rb0000644000004100000410000002074613704151031022172 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/abstract_config_value' require 'hocon/impl/abstract_config_object' require 'hocon/impl/simple_config_list' require 'hocon/config_object' require 'hocon/impl/unmergeable' require 'hocon/impl/simple_config_origin' require 'hocon/impl/config_string' require 'hocon/impl/container' class Hocon::Impl::ConfigConcatenation include Hocon::Impl::Unmergeable include Hocon::Impl::Container include Hocon::Impl::AbstractConfigValue SimpleConfigList = Hocon::Impl::SimpleConfigList ConfigObject = Hocon::ConfigObject ConfigString = Hocon::Impl::ConfigString ResolveStatus = Hocon::Impl::ResolveStatus Unmergeable = Hocon::Impl::Unmergeable SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError attr_reader :pieces def initialize(origin, pieces) super(origin) @pieces = pieces if pieces.size < 2 raise ConfigBugOrBrokenError, "Created concatenation with less than 2 items: #{self}" end had_unmergeable = false pieces.each do |p| if p.is_a?(Hocon::Impl::ConfigConcatenation) raise ConfigBugOrBrokenError, "ConfigConcatenation should never be nested: #{self}" end if p.is_a?(Unmergeable) had_unmergeable = true end end unless had_unmergeable raise ConfigBugOrBrokenError, "Created concatenation without an unmergeable in it: #{self}" end end def value_type raise not_resolved end def unwrapped raise not_resolved end def new_copy(new_origin) self.class.new(new_origin, @pieces) end def ignores_fallbacks? # we can never ignore fallbacks because if a child ConfigReference # is self-referential we have to look lower in the merge stack # for its value. false end def unmerged_values [self] end # # Add left and right, or their merger, to builder # def self.join(builder, orig_right) left = builder[builder.size - 1] right = orig_right # check for an object which can be converted to a list # (this will be an object with numeric keys, like foo.0, foo.1) if (left.is_a?(ConfigObject)) && (right.is_a?(SimpleConfigList)) left = Hocon::Impl::DefaultTransformer.transform(left, Hocon::ConfigValueType::LIST) elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(ConfigObject)) right = Hocon::Impl::DefaultTransformer.transform(right, Hocon::ConfigValueType::LIST) end # Since this depends on the type of two instances, I couldn't think # of much alternative to an instanceof chain. Visitors are sometimes # used for multiple dispatch but seems like overkill. joined = nil if (left.is_a?(ConfigObject)) && (right.is_a?(ConfigObject)) joined = right.with_fallback(left) elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(SimpleConfigList)) joined = left.concatenate(right) elsif (left.is_a?(SimpleConfigList) || left.is_a?(ConfigObject)) && is_ignored_whitespace(right) joined = left # it should be impossible that left is whitespace and right is a list or object elsif (left.is_a?(Hocon::Impl::ConfigConcatenation)) || (right.is_a?(Hocon::Impl::ConfigConcatenation)) raise ConfigBugOrBrokenError, "unflattened ConfigConcatenation" elsif (left.is_a?(Unmergeable)) || (right.is_a?(Unmergeable)) # leave joined=null, cannot join else # handle primitive type or primitive type mixed with object or list s1 = left.transform_to_string s2 = right.transform_to_string if s1.nil? || s2.nil? raise ConfigWrongTypeError.new(left.origin, "Cannot concatenate object or list with a non-object-or-list, #{left} " + "and #{right} are not compatible", nil) else joined_origin = SimpleConfigOrigin.merge_origins([left.origin, right.origin]) joined = Hocon::Impl::ConfigString::Quoted.new(joined_origin, s1 + s2) end end if joined.nil? builder.push(right) else builder.pop builder.push(joined) end end def self.consolidate(pieces) if pieces.length < 2 pieces else flattened = [] pieces.each do |v| if v.is_a?(Hocon::Impl::ConfigConcatenation) flattened.concat(v.pieces) else flattened.push(v) end end consolidated = [] flattened.each do |v| if consolidated.empty? consolidated.push(v) else join(consolidated, v) end end consolidated end end def self.concatenate(pieces) consolidated = consolidate(pieces) if consolidated.empty? nil elsif consolidated.length == 1 consolidated[0] else merged_origin = SimpleConfigOrigin.merge_value_origins(consolidated) Hocon::Impl::ConfigConcatenation.new(merged_origin, consolidated) end end def resolve_substitutions(context, source) if Hocon::Impl::ConfigImpl.trace_substitution_enabled indent = context.depth + 2 Hocon::Impl::ConfigImpl.trace("concatenation has #{@pieces.size} pieces", indent - 1) count = 0 @pieces.each { |v| Hocon::Impl::ConfigImpl.trace("#{count}: #{v}", count) count += 1 } end # Right now there's no reason to pushParent here because the # content of ConfigConcatenation should not need to replaceChild, # but if it did we'd have to do this. source_with_parent = source new_context = context resolved = [] @pieces.each { |p| # to concat into a string we have to do a full resolve, # so unrestrict the context, then put restriction back afterward restriction = new_context.restrict_to_child result = new_context.unrestricted .resolve(p, source_with_parent) r = result.value new_context = result.context.restrict(restriction) if Hocon::Impl::ConfigImpl.trace_substitution_enabled Hocon::Impl::ConfigImpl.trace("resolved concat piece to #{r}", context.depth) end if r resolved << r end # otherwise, it was optional ... omit } # now need to concat everything joined = self.class.consolidate(resolved) # if unresolved is allowed we can just become another # ConfigConcatenation if joined.size > 1 and context.options.allow_unresolved Hocon::Impl::ResolveResult.make(new_context, Hocon::Impl::ConfigConcatenation.new(origin, joined)) elsif joined.empty? # we had just a list of optional references using ${?} Hocon::Impl::ResolveResult.make(new_context, nil) elsif joined.size == 1 Hocon::Impl::ResolveResult.make(new_context, joined[0]) else raise ConfigBugOrBrokenError.new( "Bug in the library; resolved list was joined to too many values: #{joined}") end end def resolve_status ResolveStatus::UNRESOLVED end def replace_child(child, replacement) new_pieces = replace_child_in_list(@pieces, child, replacement) if new_pieces == nil nil else self.class.new(origin, new_pieces) end end def has_descendant?(descendant) has_descendant_in_list?(@pieces, descendant) end # when you graft a substitution into another object, # you have to prefix it with the location in that object # where you grafted it; but save prefixLength so # system property and env variable lookups don 't get # broken. def relativized(prefix) new_pieces = [] @pieces.each { |p| new_pieces << p.relativized(prefix) } self.class.new(origin, new_pieces) end def can_equal(other) other.is_a? Hocon::Impl::ConfigConcatenation end def ==(other) if other.is_a? Hocon::Impl::ConfigConcatenation can_equal(other) && @pieces == other.pieces else false end end def hash # note that "origin" is deliberately NOT part of equality @pieces.hash end def render_value_to_sb(sb, indent, at_root, options) @pieces.each do |piece| piece.render_value_to_sb(sb, indent, at_root, options) end end private def not_resolved ConfigNotResolvedError.new("need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: #{self}") end def self.is_ignored_whitespace(value) return value.is_a?(ConfigString) && !value.was_quoted? end end hocon-1.3.1/lib/hocon/impl/unsupported_operation_error.rb0000644000004100000410000000015213704151031023666 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' class Hocon::Impl::UnsupportedOperationError < StandardError end hocon-1.3.1/lib/hocon/impl/path.rb0000644000004100000410000001311113704151031016740 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/path_builder' require 'hocon/config_error' require 'stringio' class Hocon::Impl::Path ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError ConfigImplUtil = Hocon::Impl::ConfigImplUtil def initialize(first, remainder) # first: String, remainder: Path @first = first @remainder = remainder end attr_reader :first, :remainder def self.from_string_list(elements) # This method was translated from the Path constructor in the # Java hocon library that has this signature: # Path(String... elements) # # It figures out what @first and @remainder should be, then # pass those to the ruby constructor if elements.length == 0 raise Hocon::ConfigError::ConfigBugOrBrokenError.new("empty path") end new_first = elements.first if elements.length > 1 pb = Hocon::Impl::PathBuilder.new # Skip first element elements.drop(1).each do |element| pb.append_key(element) end new_remainder = pb.result else new_remainder = nil end self.new(new_first, new_remainder) end def self.from_path_list(path_list) # This method was translated from the Path constructors in the # Java hocon library that take in a list of Paths # # It just passes an iterator to self.from_path_iterator, which # will return a new Path object from_path_iterator(path_list.each) end def self.from_path_iterator(path_iterator) # This method was translated from the Path constructors in the # Java hocon library that takes in an iterator of Paths # # It figures out what @first and @remainder should be, then # pass those to the ruby constructor # Try to get first path from iterator # Ruby iterators have no .hasNext() method like java # So we try to catch the StopIteration exception begin first_path = path_iterator.next rescue StopIteration raise Hocon::ConfigError::ConfigBugOrBrokenError("empty path") end new_first = first_path.first pb = Hocon::Impl::PathBuilder.new unless first_path.remainder.nil? pb.append_path(first_path.remainder) end # Skip first path path_iterator.drop(1).each do |path| pb.append_path(path) end new_remainder = pb.result self.new(new_first, new_remainder) end def first @first end def remainder @remainder end def parent if remainder.nil? return nil end pb = Hocon::Impl::PathBuilder.new p = self while not p.remainder.nil? pb.append_key(p.first) p = p.remainder end pb.result end def last p = self while p.remainder != nil p = p.remainder end p.first end def prepend(to_prepend) pb = Hocon::Impl::PathBuilder.new pb.append_path(to_prepend) pb.append_path(self) pb.result end def length count = 1 p = remainder while p != nil do count += 1 p = p.remainder end count end def sub_path(first_index, last_index) if last_index < first_index raise ConfigBugOrBrokenError.new("bad call to sub_path") end from = sub_path_to_end(first_index) pb = Hocon::Impl::PathBuilder.new count = last_index - first_index while count > 0 do count -= 1 pb.append_key(from.first) from = from.remainder if from.nil? raise ConfigBugOrBrokenError.new("sub_path last_index out of range #{last_index}") end end pb.result end # translated from `subPath(int removeFromFront)` upstream def sub_path_to_end(remove_from_front) count = remove_from_front p = self while (not p.nil?) && count > 0 do count -= 1 p = p.remainder end p end def starts_with(other) my_remainder = self other_remainder = other if other_remainder.length <= my_remainder.length while ! other_remainder.nil? if ! (other_remainder.first == my_remainder.first) return false end my_remainder = my_remainder.remainder other_remainder = other_remainder.remainder end return true end false end def ==(other) if other.is_a? Hocon::Impl::Path that = other first == that.first && ConfigImplUtil.equals_handling_nil?(remainder, that.remainder) else false end end def hash remainder_hash = remainder.nil? ? 0 : remainder.hash 41 * (41 + first.hash) + remainder_hash end # this doesn't have a very precise meaning, just to reduce # noise from quotes in the rendered path for average cases def self.has_funky_chars?(s) length = s.length if length == 0 return false end s.chars.each do |c| unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_') return true end end false end def append_to_string_builder(sb) if self.class.has_funky_chars?(@first) || @first.empty? sb << ConfigImplUtil.render_json_string(@first) else sb << @first end unless @remainder.nil? sb << "." @remainder.append_to_string_builder(sb) end end def to_s sb = StringIO.new sb << "Path(" append_to_string_builder(sb) sb << ")" sb.string end def inspect to_s end # # toString() is a debugging-oriented version while this is an # error-message-oriented human-readable one. # def render sb = StringIO.new append_to_string_builder(sb) sb.string end def self.new_key(key) return self.new(key, nil) end def self.new_path(path) Hocon::Impl::PathParser.parse_path(path) end end hocon-1.3.1/lib/hocon/impl/simple_config_origin.rb0000644000004100000410000002331513704151031022200 0ustar www-datawww-data# encoding: utf-8 require 'uri' require 'hocon/impl' require 'hocon/impl/url' require 'hocon/impl/origin_type' require 'hocon/config_error' class Hocon::Impl::SimpleConfigOrigin OriginType = Hocon::Impl::OriginType def initialize(description, line_number, end_line_number, origin_type, url_or_nil, resource_or_nil, comments_or_nil) if !description raise ArgumentError, "description may not be nil" end # HACK: naming this variable with an underscore, because the upstream library # has both a member var and a no-arg method by the name "description", and # I don't think Ruby can handle that. @_description = description @line_number = line_number @end_line_number = end_line_number @origin_type = origin_type # TODO: Currently ruby hocon does not support URLs, and so this variable # is not actually a URL/URI, but a string @url_or_nil = url_or_nil @resource_or_nil = resource_or_nil @comments_or_nil = comments_or_nil end attr_reader :_description, :line_number, :end_line_number, :origin_type, :url_or_nil, :resource_or_nil, :comments_or_nil def self.new_simple(description) self.new(description, -1, -1, OriginType::GENERIC, nil, nil, nil) end def self.new_file(file_path) self.new(file_path, -1, -1, OriginType::FILE, file_path, nil, nil) end # NOTE: not porting `new_url` because we're not going to support URLs for now def self.new_resource(resource, url = nil) desc = nil if ! url.nil? desc = resource + " @ " + url.to_external_form else desc = resource end Hocon::Impl::SimpleConfigOrigin.new(desc, -1, -1, OriginType::RESOURCE, url.nil? ? nil : url.to_external_form, resource, nil) end def with_line_number(line_number) if (line_number == @line_number) and (line_number == @end_line_number) self else Hocon::Impl::SimpleConfigOrigin.new( @_description, line_number, line_number, @origin_type, @url_or_nil, @resource_or_nil, @comments_or_nil) end end def add_url(url) SimpleConfigOrigin.new(@_description, line_number, end_line_number, origin_type, url.nil? ? nil : url.to_s, resource_or_nil, comments_or_nil) end def with_comments(comments) if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil) self else Hocon::Impl::SimpleConfigOrigin.new( @_description, @line_number, @end_line_number, @origin_type, @url_or_nil, @resource_or_nil, comments) end end def prepend_comments(comments) if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil) self elsif @comments_or_nil.nil? with_comments(comments) else merged = comments + @comments_or_nil with_comments(merged) end end def append_comments(comments) if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil) self elsif comments_or_nil.nil? with_comments(comments) else merged = comments_or_nil + comments with_comments(merged) end end def description if @line_number < 0 _description elsif end_line_number == line_number "#{_description}: #{line_number}" else "#{_description}: #{line_number}-#{end_line_number}" end end def ==(other) if other.is_a? Hocon::Impl::SimpleConfigOrigin @_description == other._description && @line_number == other.line_number && @end_line_number == other.end_line_number && @origin_type == other.origin_type && Hocon::Impl::ConfigImplUtil.equals_handling_nil?(@url_or_nil, other.url_or_nil) && Hocon::Impl::ConfigImplUtil.equals_handling_nil?(@resource_or_nil, other.resource_or_nil) else false end end def hash h = 41 * (41 + @_description.hash) h = 41 * (h + @line_number) h = 41 * (h + @end_line_number) h = 41 * (h + @origin_type.hash) unless @url_or_nil.nil? h = 41 * (h + @url_or_nil.hash) end unless @resource_or_nil.nil? h = 41 * (h + @resource_or_nil.hash) end h end def to_s "ConfigOrigin(#{_description})" end def filename # TODO because we don't support URLs, this function's URL handling # is completely pointless. It will only return _description (a string that # is the file path) or nil. # It should be cleaned up if origin_type == OriginType::FILE _description elsif ! url_or_nil.nil? url = nil begin url = Hocon::Impl::Url.new(url_or_nil) rescue Hocon::Impl::Url::MalformedUrlError => e return nil end if url.get_protocol == "file" url.get_file else nil end else nil end end def url if url_or_nil.nil? nil else begin Hocon::Impl::Url.new(url_or_nil) rescue Hocon::Impl::Url::MalformedUrlError => e nil end end end def resource resource_or_nil end def comments @comments_or_nil || [] end MERGE_OF_PREFIX = "merge of " def self.remove_merge_of_prefix(desc) if desc.start_with?(MERGE_OF_PREFIX) desc = desc[MERGE_OF_PREFIX.length, desc.length - 1] end desc end def self.merge_two(a, b) merged_desc = nil merged_start_line = nil merged_end_line = nil merged_comments = nil merged_type = if a.origin_type == b.origin_type a.origin_type else Hocon::Impl::OriginType::GENERIC end # first use the "description" field which has no line numbers # cluttering it. a_desc = remove_merge_of_prefix(a._description) b_desc = remove_merge_of_prefix(b._description) if a_desc == b_desc merged_desc = a_desc if a.line_number < 0 merged_start_line = b.line_number elsif b.line_number < 0 merged_start_line = a.line_number else merged_start_line = [a.line_number, b.line_number].min end merged_end_line = [a.end_line_number, b.end_line_number].max else # this whole merge song-and-dance was intended to avoid this case # whenever possible, but we've lost. Now we have to lose some # structured information and cram into a string. # # description() method includes line numbers, so use it instead # of description field. a_full = remove_merge_of_prefix(a._description) b_full = remove_merge_of_prefix(b._description) merged_desc = "#{MERGE_OF_PREFIX}#{a_full},#{b_full}" merged_start_line = -1 merged_end_line = -1 end merged_url = if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.url_or_nil, b.url_or_nil) a.url_or_nil else nil end merged_resource = if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.resource_or_nil, b.resource_or_nil) a.resource_or_nil else nil end if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.comments_or_nil, b.comments_or_nil) merged_comments = a.comments_or_nil else merged_comments = [] if a.comments_or_nil merged_comments.concat(a.comments_or_nil) end if b.comments_or_nil merged_comments.concat(b.comments_or_nil) end end Hocon::Impl::SimpleConfigOrigin.new( merged_desc, merged_start_line, merged_end_line, merged_type, merged_url, merged_resource, merged_comments) end def self.similarity(a, b) count = 0 if a.origin_type == b.origin_type count += 1 end if a._description == b._description count += 1 # only count these if the description field (which is the file # or resource name) also matches. if a.line_number == b.line_number count += 1 end if a.end_line_number == b.end_line_number count += 1 end if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.url_or_nil, b.url_or_nil) count += 1 end if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.resource_or_nil, b.resource_or_nil) count += 1 end end count end def self.merge_three(a, b, c) if similarity(a, b) >= similarity(b, c) merge_two(merge_two(a, b), c) else merge_two(a, merge_two(b, c)) end end def self.merge_two_origins(a, b) # a, b are ConfigOrigins merge_two(a, b) end def self.merge_value_origins(stack) # stack is an array of AbstractConfigValue origins = stack.map { |v| v.origin} merge_origins(origins) end # see also 'merge_two_origins' and 'merge_three_origins' def self.merge_origins(stack) # stack is an array of ConfigOrigin if stack.empty? raise Hocon::ConfigError::ConfigBugOrBrokenError, "can't merge empty list of origins" elsif stack.length == 1 stack[0] elsif stack.length == 2 merge_two(stack[0], stack[1]) else remaining = [] stack.each do |o| remaining << o end while remaining.size > 2 c = remaining.last remaining.delete_at(remaining.size - 1) b = remaining.last remaining.delete_at(remaining.size - 1) a = remaining.last remaining.delete_at(remaining.size - 1) merged = merge_three(a, b, c) remaining << merged end # should be down to either 1 or 2 self.merge_origins(remaining) end end # NOTE: skipping 'toFields', 'toFieldsDelta', 'fieldsDelta', 'fromFields', # 'applyFieldsDelta', and 'fromBase' from upstream for now, because they appear # to be about serialization and we probably won't be supporting that. end hocon-1.3.1/lib/hocon/impl/default_transformer.rb0000644000004100000410000000706613704151031022066 0ustar www-datawww-data# encoding: utf-8 require 'hocon/impl' require 'hocon/impl/config_string' require 'hocon/config_value_type' require 'hocon/impl/config_boolean' class Hocon::Impl::DefaultTransformer ConfigValueType = Hocon::ConfigValueType ConfigString = Hocon::Impl::ConfigString ConfigBoolean = Hocon::Impl::ConfigBoolean def self.transform(value, requested) if value.value_type == ConfigValueType::STRING s = value.unwrapped case requested when ConfigValueType::NUMBER begin v = Integer(s) return ConfigInt.new(value.origin, v, s) rescue ArgumentError # try Float end begin v = Float(s) return ConfigFloat.new(value.origin, v, s) rescue ArgumentError # oh well. end when ConfigValueType::NULL if s == "null" return ConfigNull.new(value.origin) end when ConfigValueType::BOOLEAN if s == "true" || s == "yes" || s == "on" return ConfigBoolean.new(value.origin, true) elsif s == "false" || s == "no" || s == "off" return ConfigBoolean.new(value.origin, false) end when ConfigValueType::LIST # can't go STRING to LIST automatically when ConfigValueType::OBJECT # can't go STRING to OBJECT automatically when ConfigValueType::STRING # no-op STRING to STRING end elsif requested == ConfigValueType::STRING # if we converted null to string here, then you wouldn't properly # get a missing-value error if you tried to get a null value # as a string. case value.value_type # Ruby note: can't fall through in ruby. In the java code, NUMBER # just rolls over to the BOOLEAN case when ConfigValueType::NUMBER return ConfigString::Quoted.new(value.origin, value.transform_to_string) when ConfigValueType::BOOLEAN return ConfigString::Quoted.new(value.origin, value.transform_to_string) when ConfigValueType::NULL # want to be sure this throws instead of returning "null" as a # string when ConfigValueType::OBJECT # no OBJECT to STRING automatically when ConfigValueType::LIST # no LIST to STRING automatically when ConfigValueType::STRING # no-op STRING to STRING end elsif requested == ConfigValueType::LIST && value.value_type == ConfigValueType::OBJECT # attempt to convert an array-like (numeric indices) object to a # list. This would be used with .properties syntax for example: # -Dfoo.0=bar -Dfoo.1=baz # To ensure we still throw type errors for objects treated # as lists in most cases, we'll refuse to convert if the object # does not contain any numeric keys. This means we don't allow # empty objects here though :-/ o = value values = Hash.new values o.keys.each do |key| begin i = Integer(key, 10) if i < 0 next end values[key] = i rescue ArgumentError next end end if not values.empty? entry_list = values.to_a # sort by numeric index entry_list.sort! {|a,b| b[0] <=> a[0]} # drop the indices (we allow gaps in the indices, for better or # worse) list = Array.new entry_list.each do |entry| list.push(entry[1]) end return SimpleConfigList.new(value.origin, list) end end value end end hocon-1.3.1/lib/hocon/version.rb0000644000004100000410000000007513704151031016535 0ustar www-datawww-datamodule Hocon module Version STRING = '1.3.1' end end hocon-1.3.1/lib/hocon/config_factory.rb0000644000004100000410000000437413704151031020052 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl/parseable' require 'hocon/config_parse_options' require 'hocon/impl/config_impl' require 'hocon/config_factory' ## Please note that the `parse` operations will simply create a ConfigValue ## and do nothing else, whereas the `load` operations will perform a higher-level ## operation and will resolve substitutions. If you have substitutions in your ## configuration, use a `load` function class Hocon::ConfigFactory def self.parse_file(file_path, options = Hocon::ConfigParseOptions.defaults) Hocon::Impl::Parseable.new_file(file_path, options).parse.to_config end def self.parse_string(string, options = Hocon::ConfigParseOptions.defaults) Hocon::Impl::Parseable.new_string(string, options).parse.to_config end def self.parse_file_any_syntax(file_base_name, options) Hocon::Impl::ConfigImpl.parse_file_any_syntax(file_base_name, options).to_config end def self.empty(origin_description = nil) Hocon::Impl::ConfigImpl.empty_config(origin_description) end # Because of how optional arguments work, if either parse or resolve options is supplied # both must be supplied. load_file_with_parse_options or load_file_with_resolve_options # can be used instead, or the argument you don't care about in load_file can be nil # # e.g.: # load_file("settings", my_parse_options, nil) # is equivalent to: # load_file_with_parse_options("settings", my_parse_options) def self.load_file(file_base_name, parse_options = nil, resolve_options = nil) parse_options ||= Hocon::ConfigParseOptions.defaults resolve_options ||= Hocon::ConfigResolveOptions.defaults config = Hocon::ConfigFactory.parse_file_any_syntax(file_base_name, parse_options) self.load_from_config(config, resolve_options) end def self.load_file_with_parse_options(file_base_name, parse_options) self.load_file(file_base_name, parse_options, nil) end def self.load_file_with_resolve_options(file_base_name, resolve_options) self.load_file(file_base_name, nil, resolve_options) end def self.load_from_config(config, resolve_options) config.with_fallback(self.default_reference).resolve(resolve_options) end def self.default_reference Hocon::Impl::ConfigImpl.default_reference end end hocon-1.3.1/lib/hocon/impl.rb0000644000004100000410000000007313704151031016007 0ustar www-datawww-data# encoding: utf-8 require 'hocon' module Hocon::Impl endhocon-1.3.1/lib/hocon/config_include_context.rb0000644000004100000410000000350413704151031021564 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' # # Context provided to a {@link ConfigIncluder}; this interface is only useful # inside a {@code ConfigIncluder} implementation, and is not intended for apps # to implement. # #

# Do not implement this interface; it should only be implemented by # the config library. Arbitrary implementations will not work because the # library internals assume a specific concrete implementation. Also, this # interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::ConfigIncludeContext # # Tries to find a name relative to whatever is doing the including, for # example in the same directory as the file doing the including. Returns # null if it can't meaningfully create a relative name. The returned # parseable may not exist; this function is not required to do any IO, just # compute what the name would be. # # The passed-in filename has to be a complete name (with extension), not # just a basename. (Include statements in config files are allowed to give # just a basename.) # # @param filename # the name to make relative to the resource doing the including # @return parseable item relative to the resource doing the including, or # null # def relative_to(filename) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncludeContext` must implement `relative_to` (#{self.class})" end # # Parse options to use (if you use another method to get a # {@link ConfigParseable} then use {@link ConfigParseable#options()} # instead though). # # @return the parse options # def parse_options raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncludeContext` must implement `parse_options` (#{self.class})" end end hocon-1.3.1/lib/hocon/config_syntax.rb0000644000004100000410000000041413704151031017720 0ustar www-datawww-data# encoding: utf-8 require 'hocon' module Hocon::ConfigSyntax JSON = 0 CONF = 1 # alias 'HOCON' to 'CONF' since some users may be more familiar with that HOCON = 1 # we're not going to try to support .properties files any time soon :) #PROPERTIES = 2 end hocon-1.3.1/lib/hocon/config_render_options.rb0000644000004100000410000000226013704151031021425 0ustar www-datawww-data# encoding: utf-8 require 'hocon' class Hocon::ConfigRenderOptions def initialize(origin_comments, comments, formatted, json, key_value_separator=:equals) @origin_comments = origin_comments @comments = comments @formatted = formatted @json = json @key_value_separator = key_value_separator end attr_accessor :origin_comments, :comments, :formatted, :json, :key_value_separator def origin_comments? @origin_comments end def comments? @comments end def formatted? @formatted end def json? @json end # # Returns the default render options which are verbose (commented and # formatted). See {@link ConfigRenderOptions#concise} for stripped-down # options. This rendering will not be valid JSON since it has comments. # # @return the default render options # def self.defaults Hocon::ConfigRenderOptions.new(true, true, true, true) end # # Returns concise render options (no whitespace or comments). For a # resolved {@link Config}, the concise rendering will be valid JSON. # # @return the concise render options # def self.concise Hocon::ConfigRenderOptions.new(false, false, false, true) end end hocon-1.3.1/lib/hocon/config_error.rb0000644000004100000410000000332513704151031017527 0ustar www-datawww-data# encoding: utf-8 require 'hocon' class Hocon::ConfigError < StandardError def initialize(origin, message, cause) msg = if origin.nil? message else "#{origin.description}: #{message}" end super(msg) @origin = origin @cause = cause end class ConfigMissingError < Hocon::ConfigError end class ConfigNullError < Hocon::ConfigError::ConfigMissingError def self.make_message(path, expected) if not expected.nil? "Configuration key '#{path}' is set to nil but expected #{expected}" else "Configuration key '#{path}' is nil" end end end class ConfigIOError < Hocon::ConfigError def initialize(origin, message, cause = nil) super(origin, message, cause) end end class ConfigParseError < Hocon::ConfigError end class ConfigWrongTypeError < Hocon::ConfigError def self.with_expected_actual(origin, path, expected, actual, cause = nil) ConfigWrongTypeError.new(origin, "#{path} has type #{actual} rather than #{expected}", cause) end end class ConfigBugOrBrokenError < Hocon::ConfigError def initialize(message, cause = nil) super(nil, message, cause) end end class ConfigNotResolvedError < Hocon::ConfigError::ConfigBugOrBrokenError end class ConfigBadPathError < Hocon::ConfigError def initialize(origin, path, message, cause = nil) error_message = !path.nil? ? "Invalid path '#{path}': #{message}" : message super(origin, error_message, cause) end end class UnresolvedSubstitutionError < ConfigParseError def initialize(origin, detail, cause = nil) super(origin, "Could not resolve substitution to a value: " + detail, cause) end end end hocon-1.3.1/lib/hocon/config_resolve_options.rb0000644000004100000410000000116413704151031021627 0ustar www-datawww-data# encoding: utf-8 require 'hocon' class Hocon::ConfigResolveOptions attr_reader :use_system_environment, :allow_unresolved def initialize(use_system_environment, allow_unresolved) @use_system_environment = use_system_environment @allow_unresolved = allow_unresolved end def set_use_system_environment(value) self.class.new(value, @allow_unresolved) end def set_allow_unresolved(value) self.class.new(@use_system_environment, value) end class << self def defaults self.new(true, false) end def no_system defaults.set_use_system_environment(false) end end end hocon-1.3.1/lib/hocon/config_mergeable.rb0000644000004100000410000000503513704151031020321 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' # # Marker for types whose instances can be merged, that is {@link Config} and # {@link ConfigValue}. Instances of {@code Config} and {@code ConfigValue} can # be combined into a single new instance using the # {@link ConfigMergeable#withFallback withFallback()} method. # #

# Do not implement this interface; it should only be implemented by # the config library. Arbitrary implementations will not work because the # library internals assume a specific concrete implementation. Also, this # interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::ConfigMergeable # # Returns a new value computed by merging this value with another, with # keys in this value "winning" over the other one. # #

# This associative operation may be used to combine configurations from # multiple sources (such as multiple configuration files). # #

# The semantics of merging are described in the spec # for HOCON. Merging typically occurs when either the same object is # created twice in the same file, or two config files are both loaded. For # example: # #

#  foo = { a: 42 }
#  foo = { b: 43 }
# 
# # Here, the two objects are merged as if you had written: # #
#  foo = { a: 42, b: 43 }
# 
# #

# Only {@link ConfigObject} and {@link Config} instances do anything in # this method (they need to merge the fallback keys into themselves). All # other values just return the original value, since they automatically # override any fallback. This means that objects do not merge "across" # non-objects; if you write # object.withFallback(nonObject).withFallback(otherObject), # then otherObject will simply be ignored. This is an # intentional part of how merging works, because non-objects such as # strings and integers replace (rather than merging with) any prior value: # #

# foo = { a: 42 }
# foo = 10
# 
# # Here, the number 10 "wins" and the value of foo would be # simply 10. Again, for details see the spec. # # @param other # an object whose keys should be used as fallbacks, if the keys # are not present in this one # @return a new object (or the original one, if the fallback doesn't get # used) # def with_fallback(other) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigMergeable` must implement `with_fallback` (#{self.class})" end end hocon-1.3.1/lib/hocon/cli.rb0000644000004100000410000001660613704151031015626 0ustar www-datawww-datarequire 'optparse' require 'hocon' require 'hocon/version' require 'hocon/config_render_options' require 'hocon/config_factory' require 'hocon/config_value_factory' require 'hocon/parser/config_document_factory' require 'hocon/config_error' module Hocon::CLI # Aliases ConfigMissingError = Hocon::ConfigError::ConfigMissingError ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError # List of valid subcommands SUBCOMMANDS = ['get', 'set', 'unset'] # For when a path can't be found in a hocon config class MissingPathError < StandardError end # Parses the command line flags and argument # Returns a options hash with values for each option and argument def self.parse_args(args) options = {} opt_parser = OptionParser.new do |opts| subcommands = SUBCOMMANDS.join(',') opts.banner = "Usage: hocon [options] {#{subcommands}} PATH [VALUE]\n\n" + "Example usages:\n" + " hocon -i settings.conf -o new_settings.conf set some.nested.value 42\n" + " hocon -f settings.conf set some.nested.value 42\n" + " cat settings.conf | hocon get some.nested.value\n\n" + "Subcommands:\n" + " get PATH - Returns the value at the given path\n" + " set PATH VALUE - Sets or adds the given value at the given path\n" + " unset PATH - Removes the value at the given path" opts.separator('') opts.separator('Options:') in_file_description = 'HOCON file to read/modify. If omitted, STDIN assumed' opts.on('-i', '--in-file HOCON_FILE', in_file_description) do |in_file| options[:in_file] = in_file end out_file_description = 'File to be written to. If omitted, STDOUT assumed' opts.on('-o', '--out-file HOCON_FILE', out_file_description) do |out_file| options[:out_file] = out_file end file_description = 'File to read/write to. Equivalent to setting -i/-o to the same file' opts.on('-f', '--file HOCON_FILE', file_description) do |file| options[:file] = file end json_description = "Output values from the 'get' subcommand in json format" opts.on('-j', '--json', json_description) do |json| options[:json] = json end opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end opts.on_tail('-v', '--version', 'Show version') do puts Hocon::Version::STRING exit end end # parse! returns the argument list minus all the flags it found remaining_args = opt_parser.parse!(args) # Ensure -i and -o aren't used at the same time as -f if (options[:in_file] || options[:out_file]) && options[:file] exit_with_usage_and_error(opt_parser, "--file can't be used with --in-file or --out-file") end # If --file is used, set --in/out-file to the same file if options[:file] options[:in_file] = options[:file] options[:out_file] = options[:file] end no_subcommand_error(opt_parser) unless remaining_args.size > 0 # Assume the first arg is the subcommand subcommand = remaining_args.shift options[:subcommand] = subcommand case subcommand when 'set' subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 2 options[:path] = remaining_args.shift options[:new_value] = remaining_args.shift when 'get', 'unset' subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 1 options[:path] = remaining_args.shift else invalid_subcommand_error(subcommand, opt_parser) end options end # Main entry point into the script # Calls the appropriate subcommand and handles errors raised from the subcommands def self.main(opts) hocon_text = get_hocon_file(opts[:in_file]) begin case opts[:subcommand] when 'get' puts do_get(opts, hocon_text) when 'set' print_or_write(do_set(opts, hocon_text), opts[:out_file]) when 'unset' print_or_write(do_unset(opts, hocon_text), opts[:out_file]) end rescue MissingPathError exit_with_error("Can't find the given path: '#{opts[:path]}'") end exit end # Entry point for the 'get' subcommand # Returns a string representation of the the value at the path given on the # command line def self.do_get(opts, hocon_text) config = Hocon::ConfigFactory.parse_string(hocon_text) unless config.has_path?(opts[:path]) raise MissingPathError.new end value = config.get_any_ref(opts[:path]) render_options = Hocon::ConfigRenderOptions.defaults # Otherwise weird comments show up in the output render_options.origin_comments = false # If json is false, the hocon format is used render_options.json = opts[:json] # Output colons between keys and values render_options.key_value_separator = :colon Hocon::ConfigValueFactory.from_any_ref(value).render(render_options) end # Entry point for the 'set' subcommand # Returns a string representation of the HOCON config after adding/replacing # the value at the given path with the given value def self.do_set(opts, hocon_text) config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text) modified_config_doc = config_doc.set_value(opts[:path], opts[:new_value]) modified_config_doc.render end # Entry point for the 'unset' subcommand # Returns a string representation of the HOCON config after removing the # value at the given path def self.do_unset(opts, hocon_text) config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text) unless config_doc.has_value?(opts[:path]) raise MissingPathError.new end modified_config_doc = config_doc.remove_value(opts[:path]) modified_config_doc.render end # If a file is provided, return it's contents. Otherwise read from STDIN def self.get_hocon_file(in_file) if in_file File.read(in_file) else STDIN.read end end # Print an error message and exit the program def self.exit_with_error(message) STDERR.puts "Error: #{message}" exit(1) end # Print an error message and usage, then exit the program def self.exit_with_usage_and_error(opt_parser, message) STDERR.puts opt_parser exit_with_error(message) end # Exits with an error saying there aren't enough arguments found for a given # subcommand. Prints the usage def self.subcommand_arguments_error(subcommand, opt_parser) error_message = "Too few arguments for '#{subcommand}' subcommand" exit_with_usage_and_error(opt_parser, error_message) end # Exits with an error for when no subcommand is supplied on the command line. # Prints the usage def self.no_subcommand_error(opt_parser) error_message = "Must specify subcommand from [#{SUBCOMMANDS.join(', ')}]" exit_with_usage_and_error(opt_parser, error_message) end # Exits with an error for when a subcommand doesn't exist. Prints the usage def self.invalid_subcommand_error(subcommand, opt_parser) error_message = "Invalid subcommand '#{subcommand}', must be one of [#{SUBCOMMANDS.join(', ')}]" exit_with_usage_and_error(opt_parser, error_message) end # If out_file is not nil, write to that file. Otherwise print to STDOUT def self.print_or_write(string, out_file) if out_file File.open(out_file, 'w') { |file| file.write(string) } else puts string end end end hocon-1.3.1/lib/hocon/config_object.rb0000644000004100000410000001317513704151031017650 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_value' # # Subtype of {@link ConfigValue} representing an object (AKA dictionary or map) # value, as in JSON's curly brace { "a" : 42 } syntax. # #

# An object may also be viewed as a {@link Config} by calling # {@link ConfigObject#toConfig()}. # #

# {@code ConfigObject} implements {@code java.util.Map} so # you can use it like a regular Java map. Or call {@link #unwrapped()} to # unwrap the map to a map with plain Java values rather than # {@code ConfigValue}. # #

# Like all {@link ConfigValue} subtypes, {@code ConfigObject} is immutable. # This makes it threadsafe and you never have to create "defensive copies." The # mutator methods from {@link java.util.Map} all throw # {@link java.lang.UnsupportedOperationException}. # #

# The {@link ConfigValue#valueType} method on an object returns # {@link ConfigValueType#OBJECT}. # #

# In most cases you want to use the {@link Config} interface rather than this # one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a # {@code Config}. # #

# The API for a {@code ConfigObject} is in terms of keys, while the API for a # {@link Config} is in terms of path expressions. Conceptually, # {@code ConfigObject} is a tree of maps from keys to values, while a # {@code Config} is a one-level map from paths to values. # #

# Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert # between path expressions and individual path elements (keys). # #

# A {@code ConfigObject} may contain null values, which will have # {@link ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If # {@link ConfigObject#get(Object)} returns Java's null then the key was not # present in the parsed file (or wherever this value tree came from). If # {@code get("key")} returns a {@link ConfigValue} with type # {@code ConfigValueType#NULL} then the key was set to null explicitly in the # config file. # #

# Do not implement interface {@code ConfigObject}; it should only be # implemented by the config library. Arbitrary implementations will not work # because the library internals assume a specific concrete implementation. # Also, this interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::ConfigObject include Hocon::ConfigValue # # Converts this object to a {@link Config} instance, enabling you to use # path expressions to find values in the object. This is a constant-time # operation (it is not proportional to the size of the object). # # @return a {@link Config} with this object as its root # def to_config raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `to_config` (#{self.class})" end # # Recursively unwraps the object, returning a map from String to whatever # plain Java values are unwrapped from the object's values. # # @return a {@link java.util.Map} containing plain Java objects # def unwrapped raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `unwrapped` (#{self.class})" end def with_fallback(other) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_fallback` (#{self.class})" end # # Gets a {@link ConfigValue} at the given key, or returns null if there is # no value. The returned {@link ConfigValue} may have # {@link ConfigValueType#NULL} or any other type, and the passed-in key # must be a key in this object (rather than a path expression). # # @param key # key to look up # # @return the value at the key or null if none # def get(key) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `get` (#{self.class})" end # # Clone the object with only the given key (and its children) retained; all # sibling keys are removed. # # @param key # key to keep # @return a copy of the object minus all keys except the one specified # def with_only_key(key) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_only_key` (#{self.class})" end # # Clone the object with the given key removed. # # @param key # key to remove # @return a copy of the object minus the specified key # def without_key(key) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `without_key` (#{self.class})" end # # Returns a {@code ConfigObject} based on this one, but with the given key # set to the given value. Does not modify this instance (since it's # immutable). If the key already has a value, that value is replaced. To # remove a value, use {@link ConfigObject#withoutKey(String)}. # # @param key # key to add # @param value # value at the new key # @return the new instance with the new map entry # def with_value(key, value) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_value` (#{self.class})" end def with_origin(origin) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_origin` (#{self.class})" end end hocon-1.3.1/lib/hocon/config_util.rb0000644000004100000410000000431513704151031017353 0ustar www-datawww-datarequire 'hocon/impl/config_impl_util' # Contains static utility methods class Hocon::ConfigUtil # # Quotes and escapes a string, as in the JSON specification. # # @param string # a string # @return the string quoted and escaped # def self.quote_string(string) Hocon::Impl::ConfigImplUtil.render_json_string(string) end # # Converts a list of keys to a path expression, by quoting the path # elements as needed and then joining them separated by a period. A path # expression is usable with a {@link Config}, while individual path # elements are usable with a {@link ConfigObject}. #

# See the overview documentation for {@link Config} for more detail on path # expressions vs. keys. # # @param elements # the keys in the path # @return a path expression # @throws ConfigException # if there are no elements # def self.join_path(*elements) Hocon::Impl::ConfigImplUtil.join_path(*elements) end # # Converts a list of strings to a path expression, by quoting the path # elements as needed and then joining them separated by a period. A path # expression is usable with a {@link Config}, while individual path # elements are usable with a {@link ConfigObject}. #

# See the overview documentation for {@link Config} for more detail on path # expressions vs. keys. # # @param elements # the keys in the path # @return a path expression # @throws ConfigException # if the list is empty # def self.join_path_from_list(elements) self.join_path(*elements) end # # Converts a path expression into a list of keys, by splitting on period # and unquoting the individual path elements. A path expression is usable # with a {@link Config}, while individual path elements are usable with a # {@link ConfigObject}. #

# See the overview documentation for {@link Config} for more detail on path # expressions vs. keys. # # @param path # a path expression # @return the individual keys in the path # @throws ConfigException # if the path expression is invalid # def self.split_path(path) Hocon::Impl::ConfigImplUtil.split_path(path) end end hocon-1.3.1/lib/hocon/parser.rb0000644000004100000410000000007513704151031016344 0ustar www-datawww-data# encoding: utf-8 require 'hocon' module Hocon::Parser endhocon-1.3.1/lib/hocon/config_parse_options.rb0000644000004100000410000000352713704151031021267 0ustar www-datawww-data# encoding: utf-8 require 'hocon' class Hocon::ConfigParseOptions attr_accessor :syntax, :origin_description, :allow_missing, :includer def self.defaults self.new(nil, nil, true, nil) end def initialize(syntax, origin_description, allow_missing, includer) @syntax = syntax @origin_description = origin_description @allow_missing = allow_missing @includer = includer end def set_syntax(syntax) if @syntax == syntax self else Hocon::ConfigParseOptions.new(syntax, @origin_description, @allow_missing, @includer) end end def set_origin_description(origin_description) if @origin_description == origin_description self else Hocon::ConfigParseOptions.new(@syntax, origin_description, @allow_missing, @includer) end end def set_allow_missing(allow_missing) if allow_missing? == allow_missing self else Hocon::ConfigParseOptions.new(@syntax, @origin_description, allow_missing, @includer) end end def allow_missing? @allow_missing end def set_includer(includer) if @includer == includer self else Hocon::ConfigParseOptions.new(@syntax, @origin_description, @allow_missing, includer) end end def append_includer(includer) if @includer == includer self elsif @includer set_includer(@includer.with_fallback(includer)) else set_includer(includer) end end end hocon-1.3.1/lib/hocon/config_list.rb0000644000004100000410000000320613704151031017347 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_value' require 'hocon/config_error' # # Subtype of {@link ConfigValue} representing a list value, as in JSON's # {@code [1,2,3]} syntax. # #

# {@code ConfigList} implements {@code java.util.List} so you can # use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the # list elements into plain Java values. # #

# Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This # makes it threadsafe and you never have to create "defensive copies." The # mutator methods from {@link java.util.List} all throw # {@link java.lang.UnsupportedOperationException}. # #

# The {@link ConfigValue#valueType} method on a list returns # {@link ConfigValueType#LIST}. # #

# Do not implement {@code ConfigList}; it should only be implemented # by the config library. Arbitrary implementations will not work because the # library internals assume a specific concrete implementation. Also, this # interface is likely to grow new methods over time, so third-party # implementations will break. # # module Hocon::ConfigList include Hocon::ConfigValue # # Recursively unwraps the list, returning a list of plain Java values such # as Integer or String or whatever is in the list. # def unwrapped raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigValue should provide their own implementation of `unwrapped` (#{self.class})" end def with_origin(origin) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigValue should provide their own implementation of `with_origin` (#{self.class})" end end hocon-1.3.1/lib/hocon/config_parseable.rb0000644000004100000410000000317713704151031020341 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' # # An opaque handle to something that can be parsed, obtained from # {@link ConfigIncludeContext}. # #

# Do not implement this interface; it should only be implemented by # the config library. Arbitrary implementations will not work because the # library internals assume a specific concrete implementation. Also, this # interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::ConfigParseable # # Parse whatever it is. The options should come from # {@link ConfigParseable#options options()} but you could tweak them if you # like. # # @param options # parse options, should be based on the ones from # {@link ConfigParseable#options options()} # @return the parsed object # def parse(options) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `parse` (#{self.class})" end # # Returns a {@link ConfigOrigin} describing the origin of the parseable # item. # @return the origin of the parseable item # def origin raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `origin` (#{self.class})" end # # Get the initial options, which can be modified then passed to parse(). # These options will have the right description, includer, and other # parameters already set up. # @return the initial options # def options raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `options` (#{self.class})" end end hocon-1.3.1/lib/hocon/config_includer_file.rb0000644000004100000410000000171713704151031021205 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_error' # # Implement this in addition to {@link ConfigIncluder} if you want to # support inclusion of files with the {@code include file("filename")} syntax. # If you do not implement this but do implement {@link ConfigIncluder}, # attempts to load files will use the default includer. # module Hocon::ConfigIncluderFile # # Parses another item to be included. The returned object typically would # not have substitutions resolved. You can throw a ConfigException here to # abort parsing, or return an empty object, but may not return null. # # @param context # some info about the include context # @param what # the include statement's argument # @return a non-null ConfigObject # def include_file(context, what) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncluderFile` must implement `include_file` (#{self.class})" end end hocon-1.3.1/lib/hocon/config.rb0000644000004100000410000011473513704151031016326 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_mergeable' require 'hocon/config_error' # # An immutable map from config paths to config values. Paths are dot-separated # expressions such as foo.bar.baz. Values are as in JSON # (booleans, strings, numbers, lists, or objects), represented by # {@link ConfigValue} instances. Values accessed through the # Config interface are never null. # #

# {@code Config} is an immutable object and thus safe to use from multiple # threads. There's never a need for "defensive copies." # #

# Fundamental operations on a {@code Config} include getting configuration # values, resolving substitutions with {@link Config#resolve()}, and # merging configs using {@link Config#withFallback(ConfigMergeable)}. # #

# All operations return a new immutable {@code Config} rather than modifying # the original instance. # #

# Examples # #

# You can find an example app and library on # GitHub. Also be sure to read the package overview which # describes the big picture as shown in those examples. # #

# Paths, keys, and Config vs. ConfigObject # #

# Config is a view onto a tree of {@link ConfigObject}; the # corresponding object tree can be found through {@link Config#root()}. # ConfigObject is a map from config keys, rather than # paths, to config values. Think of ConfigObject as a JSON object # and Config as a configuration API. # #

# The API tries to consistently use the terms "key" and "path." A key is a key # in a JSON object; it's just a string that's the key in a map. A "path" is a # parseable expression with a syntax and it refers to a series of keys. Path # expressions are described in the spec for # Human-Optimized Config Object Notation. In brief, a path is # period-separated so "a.b.c" looks for key c in object b in object a in the # root object. Sometimes double quotes are needed around special characters in # path expressions. # #

# The API for a {@code Config} is in terms of path expressions, while the API # for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config} # is a one-level map from paths to values, while a # {@code ConfigObject} is a tree of nested maps from keys to values. # #

# Use {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert # between path expressions and individual path elements (keys). # #

# Another difference between {@code Config} and {@code ConfigObject} is that # conceptually, {@code ConfigValue}s with a {@link ConfigValue#valueType() # valueType()} of {@link ConfigValueType#NULL NULL} exist in a # {@code ConfigObject}, while a {@code Config} treats null values as if they # were missing. # #

# Getting configuration values # #

# The "getters" on a {@code Config} all work in the same way. They never return # null, nor do they return a {@code ConfigValue} with # {@link ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL # NULL}. Instead, they throw {@link ConfigException.Missing} if the value is # completely absent or set to null. If the value is set to null, a subtype of # {@code ConfigException.Missing} called {@link ConfigException.Null} will be # thrown. {@link ConfigException.WrongType} will be thrown anytime you ask for # a type and the value has an incompatible type. Reasonable type conversions # are performed for you though. # #

# Iteration # #

# If you want to iterate over the contents of a {@code Config}, you can get its # {@code ConfigObject} with {@link #root()}, and then iterate over the # {@code ConfigObject} (which implements java.util.Map). Or, you # can use {@link #entrySet()} which recurses the object tree for you and builds # up a Set of all path-value pairs where the value is not null. # #

# Resolving substitutions # #

# Substitutions are the ${foo.bar} syntax in config # files, described in the specification. Resolving substitutions replaces these references with real # values. # #

# Before using a {@code Config} it's necessary to call {@link Config#resolve()} # to handle substitutions (though {@link ConfigFactory#load()} and similar # methods will do the resolve for you already). # #

# Merging # #

# The full Config for your application can be constructed using # the associative operation {@link Config#withFallback(ConfigMergeable)}. If # you use {@link ConfigFactory#load()} (recommended), it merges system # properties over the top of application.conf over the top of # reference.conf, using withFallback. You can add in # additional sources of configuration in the same way (usually, custom layers # should go either just above or just below application.conf, # keeping reference.conf at the bottom and system properties at # the top). # #

# Serialization # #

# Convert a Config to a JSON or HOCON string by calling # {@link ConfigObject#render()} on the root object, # myConfig.root().render(). There's also a variant # {@link ConfigObject#render(ConfigRenderOptions)} which allows you to control # the format of the rendered string. (See {@link ConfigRenderOptions}.) Note # that Config does not remember the formatting of the original # file, so if you load, modify, and re-save a config file, it will be # substantially reformatted. # #

# As an alternative to {@link ConfigObject#render()}, the # toString() method produces a debug-output-oriented # representation (which is not valid JSON). # #

# Java serialization is supported as well for Config and all # subtypes of ConfigValue. # #

# This is an interface but don't implement it yourself # #

# Do not implement {@code Config}; it should only be implemented by # the config library. Arbitrary implementations will not work because the # library internals assume a specific concrete implementation. Also, this # interface is likely to grow new methods over time, so third-party # implementations will break. # class Config < Hocon::ConfigMergeable # # Gets the {@code Config} as a tree of {@link ConfigObject}. This is a # constant-time operation (it is not proportional to the number of values # in the {@code Config}). # # @return the root object in the configuration # def root raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `root` (#{self.class})" end # # Gets the origin of the {@code Config}, which may be a file, or a file # with a line number, or just a descriptive phrase. # # @return the origin of the {@code Config} for use in error messages # def origin raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `origin` (#{self.class})" end def with_fallback(other) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_fallback` (#{self.class})" end # # Returns a replacement config with all substitutions (the # ${foo.bar} syntax, see the # spec) resolved. Substitutions are looked up using this # Config as the root object, that is, a substitution # ${foo.bar} will be replaced with the result of # getValue("foo.bar"). # #

# This method uses {@link ConfigResolveOptions#defaults()}, there is # another variant {@link Config#resolve(ConfigResolveOptions)} which lets # you specify non-default options. # #

# A given {@link Config} must be resolved before using it to retrieve # config values, but ideally should be resolved one time for your entire # stack of fallbacks (see {@link Config#withFallback}). Otherwise, some # substitutions that could have resolved with all fallbacks available may # not resolve, which will be potentially confusing for your application's # users. # #

# resolve() should be invoked on root config objects, rather # than on a subtree (a subtree is the result of something like # config.getConfig("foo")). The problem with # resolve() on a subtree is that substitutions are relative to # the root of the config and the subtree will have no way to get values # from the root. For example, if you did # config.getConfig("foo").resolve() on the below config file, # it would not work: # #

  #   common-value = 10
  #   foo {
  #      whatever = ${common-value}
  #   }
  # 
# #

# Many methods on {@link ConfigFactory} such as # {@link ConfigFactory#load()} automatically resolve the loaded # Config on the loaded stack of config files. # #

# Resolving an already-resolved config is a harmless no-op, but again, it # is best to resolve an entire stack of fallbacks (such as all your config # files combined) rather than resolving each one individually. # # @return an immutable object with substitutions resolved # @throws ConfigException.UnresolvedSubstitution # if any substitutions refer to nonexistent paths # @throws ConfigException # some other config exception if there are other problems # def resolve(options) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolve` (#{self.class})" end # # Checks whether the config is completely resolved. After a successful call # to {@link Config#resolve()} it will be completely resolved, but after # calling {@link Config#resolve(ConfigResolveOptions)} with # allowUnresolved set in the options, it may or may not be # completely resolved. A newly-loaded config may or may not be completely # resolved depending on whether there were substitutions present in the # file. # # @return true if there are no unresolved substitutions remaining in this # configuration. # @since 1.2.0 # def resolved? raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolved?` (#{self.class})" end # # Like {@link Config#resolveWith(Config)} but allows you to specify # non-default options. # # @param source # source configuration to pull values from # @param options # resolve options # @return the resolved Config (may be only partially resolved # if options are set to allow unresolved) # @since 1.2.0 # def resolve_with(source, options) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolve_with` (#{self.class})" end # # Validates this config against a reference config, throwing an exception # if it is invalid. The purpose of this method is to "fail early" with a # comprehensive list of problems; in general, anything this method can find # would be detected later when trying to use the config, but it's often # more user-friendly to fail right away when loading the config. # #

# Using this method is always optional, since you can "fail late" instead. # #

# You must restrict validation to paths you "own" (those whose meaning are # defined by your code module). If you validate globally, you may trigger # errors about paths that happen to be in the config but have nothing to do # with your module. It's best to allow the modules owning those paths to # validate them. Also, if every module validates only its own stuff, there # isn't as much redundant work being done. # #

# If no paths are specified in checkValid()'s parameter list, # validation is for the entire config. # #

# If you specify paths that are not in the reference config, those paths # are ignored. (There's nothing to validate.) # #

# Here's what validation involves: # #

# #

# If you want to allow a certain setting to have a flexible type (or # otherwise want validation to be looser for some settings), you could # either remove the problematic setting from the reference config provided # to this method, or you could intercept the validation exception and # screen out certain problems. Of course, this will only work if all other # callers of this method are careful to restrict validation to their own # paths, as they should be. # #

# If validation fails, the thrown exception contains a list of all problems # found. See {@link ConfigException.ValidationFailed#problems}. The # exception's getMessage() will have all the problems # concatenated into one huge string, as well. # #

# Again, checkValid() can't guess every domain-specific way a # setting can be invalid, so some problems may arise later when attempting # to use the config. checkValid() is limited to reporting # generic, but common, problems such as missing settings and blatant type # incompatibilities. # # @param reference # a reference configuration # @param restrictToPaths # only validate values underneath these paths that your code # module owns and understands # @throws ConfigException.ValidationFailed # if there are any validation issues # @throws ConfigException.NotResolved # if this config is not resolved # @throws ConfigException.BugOrBroken # if the reference config is unresolved or caller otherwise # misuses the API # def check_valid(reference, restrict_to_paths) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `check_valid` (#{self.class})" end # # Checks whether a value is present and non-null at the given path. This # differs in two ways from {@code Map.containsKey()} as implemented by # {@link ConfigObject}: it looks for a path expression, not a key; and it # returns false for null values, while {@code containsKey()} returns true # indicating that the object contains a null value for the key. # #

# If a path exists according to {@link #hasPath(String)}, then # {@link #getValue(String)} will never throw an exception. However, the # typed getters, such as {@link #getInt(String)}, will still throw if the # value is not convertible to the requested type. # #

# Note that path expressions have a syntax and sometimes require quoting # (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param path # the path expression # @return true if a non-null value is present at the path # @throws ConfigException.BadPath # if the path expression is invalid # def has_path(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `has_path` (#{self.class})" end # # Returns true if the {@code Config}'s root object contains no key-value # pairs. # # @return true if the configuration is empty # def empty? raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `empty` (#{self.class})" end # # Returns the set of path-value pairs, excluding any null values, found by # recursing {@link #root() the root object}. Note that this is very # different from root().entrySet() which returns the set of # immediate-child keys in the root object and includes null values. #

# Entries contain path expressions meaning there may be quoting # and escaping involved. Parse path expressions with # {@link ConfigUtil#splitPath}. #

# Because a Config is conceptually a single-level map from # paths to values, there will not be any {@link ConfigObject} values in the # entries (that is, all entries represent leaf nodes). Use # {@link ConfigObject} rather than Config if you want a tree. # (OK, this is a slight lie: Config entries may contain # {@link ConfigList} and the lists may contain objects. But no objects are # directly included as entry values.) # # @return set of paths with non-null values, built up by recursing the # entire tree of {@link ConfigObject} and creating an entry for # each leaf value. # def entry_set raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `entry_set` (#{self.class})" end # # # @param path # path expression # @return the boolean value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to boolean # def get_boolean(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_boolean` (#{self.class})" end # # @param path # path expression # @return the numeric value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a number # def get_number(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_number` (#{self.class})" end # # Gets the integer at the given path. If the value at the # path has a fractional (floating point) component, it # will be discarded and only the integer part will be # returned (it works like a "narrowing primitive conversion" # in the Java language specification). # # @param path # path expression # @return the 32-bit integer value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to an int (for example it is out # of range, or it's a boolean value) # def get_int(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_int` (#{self.class})" end # # Gets the long integer at the given path. If the value at # the path has a fractional (floating point) component, it # will be discarded and only the integer part will be # returned (it works like a "narrowing primitive conversion" # in the Java language specification). # # @param path # path expression # @return the 64-bit long value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a long # def get_long(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_long` (#{self.class})" end # # @param path # path expression # @return the floating-point value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a double # def get_double(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_double` (#{self.class})" end # # @param path # path expression # @return the string value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a string # def get_string(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_string` (#{self.class})" end # # @param path # path expression # @return the {@link ConfigObject} value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to an object # def get_object(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_object` (#{self.class})" end # # @param path # path expression # @return the nested {@code Config} value at the requested path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a Config # def get_config(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_config` (#{self.class})" end # # Gets the value at the path as an unwrapped Java boxed value ( # {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and # so on - see {@link ConfigValue#unwrapped()}). # # @param path # path expression # @return the unwrapped value at the requested path # @throws ConfigException.Missing # if value is absent or null # def get_any_ref(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_any_ref` (#{self.class})" end # # Gets the value at the given path, unless the value is a # null value or missing, in which case it throws just like # the other getters. Use {@code get()} on the {@link # Config#root()} object (or other object in the tree) if you # want an unprocessed value. # # @param path # path expression # @return the value at the requested path # @throws ConfigException.Missing # if value is absent or null # def get_value(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_value`path(#{self.class})" end # # Gets a value as a size in bytes (parses special strings like "128M"). If # the value is already a number, then it's left alone; if it's a string, # it's parsed understanding unit suffixes such as "128K", as documented in # the the # spec. # # @param path # path expression # @return the value at the requested path, in bytes # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to Long or String # @throws ConfigException.BadValue # if value cannot be parsed as a size in bytes # def get_bytes(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_bytes` (#{self.class})" end # # Gets a value as an amount of memory (parses special strings like "128M"). If # the value is already a number, then it's left alone; if it's a string, # it's parsed understanding unit suffixes such as "128K", as documented in # the the # spec. # # @since 1.3.0 # # @param path # path expression # @return the value at the requested path, in bytes # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to Long or String # @throws ConfigException.BadValue # if value cannot be parsed as a size in bytes # def get_memory_size(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_memory_size`path(#{self.class})" end # # Get value as a duration in milliseconds. If the value is already a # number, then it's left alone; if it's a string, it's parsed understanding # units suffixes like "10m" or "5ns" as documented in the the # spec. # # @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} # # @param path # path expression # @return the duration value at the requested path, in milliseconds # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to Long or String # @throws ConfigException.BadValue # if value cannot be parsed as a number of milliseconds # def get_milliseconds(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_milliseconds` (#{self.class})" end # # Get value as a duration in nanoseconds. If the value is already a number # it's taken as milliseconds and converted to nanoseconds. If it's a # string, it's parsed understanding unit suffixes, as for # {@link #getDuration(String, TimeUnit)}. # # @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} # # @param path # path expression # @return the duration value at the requested path, in nanoseconds # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to Long or String # @throws ConfigException.BadValue # if value cannot be parsed as a number of nanoseconds # def get_nanoseconds(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_nanoseconds` (#{self.class})" end # # Gets a value as a duration in a specified # {@link java.util.concurrent.TimeUnit TimeUnit}. If the value is already a # number, then it's taken as milliseconds and then converted to the # requested TimeUnit; if it's a string, it's parsed understanding units # suffixes like "10m" or "5ns" as documented in the the # spec. # # @since 1.2.0 # # @param path # path expression # @param unit # convert the return value to this time unit # @return the duration value at the requested path, in the given TimeUnit # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to Long or String # @throws ConfigException.BadValue # if value cannot be parsed as a number of the given TimeUnit # def get_duration(path, unit) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_duration` (#{self.class})" end # # Gets a list value (with any element type) as a {@link ConfigList}, which # implements {@code java.util.List}. Throws if the path is # unset or null. # # @param path # the path to the list value. # @return the {@link ConfigList} at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a ConfigList # def get_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_list` (#{self.class})" end # # Gets a list value with boolean elements. Throws if the # path is unset or null or not a list or contains values not # convertible to boolean. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of booleans # def get_boolean_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_boolean_list` (#{self.class})" end # # Gets a list value with number elements. Throws if the # path is unset or null or not a list or contains values not # convertible to number. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of numbers # def get_number_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_number_list` (#{self.class})" end # # Gets a list value with int elements. Throws if the # path is unset or null or not a list or contains values not # convertible to int. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of ints # def get_int_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_int_list` (#{self.class})" end # # Gets a list value with long elements. Throws if the # path is unset or null or not a list or contains values not # convertible to long. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of longs # def get_long_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_long_list` (#{self.class})" end # # Gets a list value with double elements. Throws if the # path is unset or null or not a list or contains values not # convertible to double. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of doubles # def get_double_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_double_list` (#{self.class})" end # # Gets a list value with string elements. Throws if the # path is unset or null or not a list or contains values not # convertible to string. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of strings # def get_string_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_string_list` (#{self.class})" end # # Gets a list value with object elements. Throws if the # path is unset or null or not a list or contains values not # convertible to ConfigObject. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of objects # def get_object_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_object_list` (#{self.class})" end # # Gets a list value with Config elements. # Throws if the path is unset or null or not a list or # contains values not convertible to Config. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of configs # def get_config_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_config_list` (#{self.class})" end # # Gets a list value with any kind of elements. Throws if the # path is unset or null or not a list. Each element is # "unwrapped" (see {@link ConfigValue#unwrapped()}). # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list # def get_any_ref_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_any_ref_list`path(#{self.class})" end # # Gets a list value with elements representing a size in # bytes. Throws if the path is unset or null or not a list # or contains values not convertible to memory sizes. # # @param path # the path to the list value. # @return the list at the path # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of memory sizes # def get_bytes_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_bytes_list` (#{self.class})" end # # Gets a list, converting each value in the list to a memory size, using the # same rules as {@link #getMemorySize(String)}. # # @since 1.3.0 # @param path # a path expression # @return list of memory sizes # @throws ConfigException.Missing # if value is absent or null # @throws ConfigException.WrongType # if value is not convertible to a list of memory sizes # def get_memory_size_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_memory_size_list` (#{self.class})" end # # @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} # @param path the path # @return list of millisecond values # def get_milliseconds_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_milliseconds_list` (#{self.class})" end # # @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} # @param path the path # @return list of nanosecond values # def get_nanoseconds_list(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_nanoseconds_list` (#{self.class})" end # # Gets a list, converting each value in the list to a duration, using the # same rules as {@link #getDuration(String, TimeUnit)}. # # @since 1.2.0 # @param path # a path expression # @param unit # time units of the returned values # @return list of durations, in the requested units # def get_duration_list(path, unit) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_duration_list` (#{self.class})" end # # Clone the config with only the given path (and its children) retained; # all sibling paths are removed. #

# Note that path expressions have a syntax and sometimes require quoting # (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param path # path to keep # @return a copy of the config minus all paths except the one specified # def with_only_path(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_only_path` (#{self.class})" end # # Clone the config with the given path removed. #

# Note that path expressions have a syntax and sometimes require quoting # (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param path # path expression to remove # @return a copy of the config minus the specified path # def without_path(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `without_path` (#{self.class})" end # # Places the config inside another {@code Config} at the given path. #

# Note that path expressions have a syntax and sometimes require quoting # (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param path # path expression to store this config at. # @return a {@code Config} instance containing this config at the given # path. # def at_path(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `at_path` (#{self.class})" end # # Places the config inside a {@code Config} at the given key. See also # atPath(). Note that a key is NOT a path expression (see # {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param key # key to store this config at. # @return a {@code Config} instance containing this config at the given # key. # def at_key(key) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `at_key` (#{self.class})" end # # Returns a {@code Config} based on this one, but with the given path set # to the given value. Does not modify this instance (since it's immutable). # If the path already has a value, that value is replaced. To remove a # value, use withoutPath(). #

# Note that path expressions have a syntax and sometimes require quoting # (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). # # @param path # path expression for the value's new location # @param value # value at the new path # @return the new instance with the new map entry # def with_value(path, value) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_value` (#{self.class})" end end hocon-1.3.1/lib/hocon/config_value_factory.rb0000644000004100000410000000600013704151031021232 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/impl/config_impl' class Hocon::ConfigValueFactory ConfigImpl = Hocon::Impl::ConfigImpl # # Creates a {@link ConfigValue} from a plain value, which may be # a Boolean, Number, String, # Hash, or nil. A # Hash must be a Hash from String to more values # that can be supplied to from_any_ref(). A Hash # will become a {@link ConfigObject} and an Array will become a # {@link ConfigList}. # #

# In a Hash passed to from_any_ref(), the map's keys # are plain keys, not path expressions. So if your Hash has a # key "foo.bar" then you will get one object with a key called "foo.bar", # rather than an object with a key "foo" containing another object with a # key "bar". # #

# The origin_description will be used to set the origin() field on the # ConfigValue. It should normally be the name of the file the values came # from, or something short describing the value such as "default settings". # The origin_description is prefixed to error messages so users can tell # where problematic values are coming from. # #

# Supplying the result of ConfigValue.unwrapped() to this function is # guaranteed to work and should give you back a ConfigValue that matches # the one you unwrapped. The re-wrapped ConfigValue will lose some # information that was present in the original such as its origin, but it # will have matching values. # #

# If you pass in a ConfigValue to this # function, it will be returned unmodified. (The # origin_description will be ignored in this # case.) # #

# This function throws if you supply a value that cannot be converted to a # ConfigValue, but supplying such a value is a bug in your program, so you # should never handle the exception. Just fix your program (or report a bug # against this library). # # @param object # object to convert to ConfigValue # @param origin_description # name of origin file or brief description of what the value is # @return a new value # def self.from_any_ref(object, origin_description = nil) if object.is_a?(Hash) from_map(object, origin_description) else ConfigImpl.from_any_ref(object, origin_description) end end # # See the {@link #from_any_ref(Object,String)} documentation for details # #

# See also {@link ConfigFactory#parse_map(Map)} which interprets the keys in # the map as path expressions. # # @param values map from keys to plain ruby values # @return a new {@link ConfigObject} # def self.from_map(values, origin_description = nil) ConfigImpl.from_any_ref(process_hash(values), origin_description) end private def self.process_hash(hash) Hash[hash.map {|k, v| [k.is_a?(Symbol) ? k.to_s : k, v.is_a?(Hash) ? process_hash(v) : v]}] end end hocon-1.3.1/lib/hocon/config_value.rb0000644000004100000410000001017713704151031017515 0ustar www-datawww-data# encoding: utf-8 require 'hocon' require 'hocon/config_mergeable' # # An immutable value, following the JSON type # schema. # #

# Because this object is immutable, it is safe to use from multiple threads and # there's no need for "defensive copies." # #

# Do not implement interface {@code ConfigValue}; it should only be # implemented by the config library. Arbitrary implementations will not work # because the library internals assume a specific concrete implementation. # Also, this interface is likely to grow new methods over time, so third-party # implementations will break. # module Hocon::ConfigValue include Hocon::ConfigMergeable # # The origin of the value (file, line number, etc.), for debugging and # error messages. # # @return where the value came from # def origin raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `origin` (#{self.class})" end # # The {@link ConfigValueType} of the value; matches the JSON type schema. # # @return value's type # def value_type raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `value_type` (#{self.class})" end # # Returns the value as a plain Java boxed value, that is, a {@code String}, # {@code Number}, {@code Boolean}, {@code Map}, # {@code List}, or {@code null}, matching the {@link #valueType()} # of this {@code ConfigValue}. If the value is a {@link ConfigObject} or # {@link ConfigList}, it is recursively unwrapped. # @return a plain Java value corresponding to this ConfigValue # def unwrapped raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `unwrapped` (#{self.class})" end # # Renders the config value to a string, using the provided options. # #

# If the config value has not been resolved (see {@link Config#resolve}), # it's possible that it can't be rendered as valid HOCON. In that case the # rendering should still be useful for debugging but you might not be able # to parse it. If the value has been resolved, it will always be parseable. # #

# If the config value has been resolved and the options disable all # HOCON-specific features (such as comments), the rendering will be valid # JSON. If you enable HOCON-only features such as comments, the rendering # will not be valid JSON. # # @param options # the rendering options # @return the rendered value # def render(options) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `render` (#{self.class})" end def with_fallback(other) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `with_fallback` (#{self.class})" end # # Places the value inside a {@link Config} at the given path. See also # {@link ConfigValue#atKey(String)}. # # @param path # path to store this value at. # @return a {@code Config} instance containing this value at the given # path. # def at_path(path) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `at_path` (#{self.class})" end # # Places the value inside a {@link Config} at the given key. See also # {@link ConfigValue#atPath(String)}. # # @param key # key to store this value at. # @return a {@code Config} instance containing this value at the given key. # def at_key(key) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `at_key` (#{self.class})" end # # Returns a {@code ConfigValue} based on this one, but with the given # origin. This is useful when you are parsing a new format of file or setting # comments for a single ConfigValue. # # @since 1.3.0 # # @param origin the origin set on the returned value # @return the new ConfigValue with the given origin # def with_origin(origin) raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `with_origin` (#{self.class})" end end hocon-1.3.1/lib/hocon.rb0000644000004100000410000000420413704151031015046 0ustar www-datawww-data# encoding: utf-8 module Hocon # NOTE: the behavior of this load method differs a bit from the upstream public # API, where a file extension may be the preferred method of determining # the config syntax, even if you specify a Syntax value on ConfigParseOptions. # Here we prefer the syntax (optionally) specified by the user no matter what # the file extension is, and if they don't specify one and the file extension # is unrecognized, we raise an error. def self.load(file, opts = nil) # doing these requires lazily, because otherwise, classes that need to # `require 'hocon'` to get the module into scope will end up recursing # through this require and probably ending up with circular dependencies. require 'hocon/config_factory' require 'hocon/impl/parseable' require 'hocon/config_parse_options' require 'hocon/config_resolve_options' require 'hocon/config_error' syntax = opts ? opts[:syntax] : nil if syntax.nil? unless Hocon::Impl::Parseable.syntax_from_extension(file) raise Hocon::ConfigError::ConfigParseError.new( nil, "Unrecognized file extension '#{File.extname(file)}' and no value provided for :syntax option", nil) end config = Hocon::ConfigFactory.parse_file_any_syntax( file, Hocon::ConfigParseOptions.defaults) else config = Hocon::ConfigFactory.parse_file( file, Hocon::ConfigParseOptions.defaults.set_syntax(syntax)) end resolved_config = Hocon::ConfigFactory.load_from_config( config, Hocon::ConfigResolveOptions.defaults) resolved_config.root.unwrapped end def self.parse(string) # doing these requires lazily, because otherwise, classes that need to # `require 'hocon'` to get the module into scope will end up recursing # through this require and probably ending up with circular dependencies. require 'hocon/config_factory' require 'hocon/config_resolve_options' config = Hocon::ConfigFactory.parse_string(string) resolved_config = Hocon::ConfigFactory.load_from_config( config, Hocon::ConfigResolveOptions.defaults) resolved_config.root.unwrapped end end hocon-1.3.1/hocon.gemspec0000644000004100000410000001263313704151031015325 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: hocon 1.3.1 ruby lib Gem::Specification.new do |s| s.name = "hocon".freeze s.version = "1.3.1" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Chris Price".freeze, "Wayne Warren".freeze, "Preben Ingvaldsen".freeze, "Joe Pinsonault".freeze, "Kevin Corcoran".freeze, "Jane Lu".freeze] s.date = "2016-10-27" s.description = "== A port of the Java {Typesafe Config}[https://github.com/typesafehub/config] library to Ruby".freeze s.email = "chris@puppetlabs.com".freeze s.executables = ["hocon".freeze] s.files = ["CHANGELOG.md".freeze, "HISTORY.md".freeze, "LICENSE".freeze, "README.md".freeze, "bin/hocon".freeze, "lib/hocon.rb".freeze, "lib/hocon/cli.rb".freeze, "lib/hocon/config.rb".freeze, "lib/hocon/config_error.rb".freeze, "lib/hocon/config_factory.rb".freeze, "lib/hocon/config_include_context.rb".freeze, "lib/hocon/config_includer_file.rb".freeze, "lib/hocon/config_list.rb".freeze, "lib/hocon/config_mergeable.rb".freeze, "lib/hocon/config_object.rb".freeze, "lib/hocon/config_parse_options.rb".freeze, "lib/hocon/config_parseable.rb".freeze, "lib/hocon/config_render_options.rb".freeze, "lib/hocon/config_resolve_options.rb".freeze, "lib/hocon/config_syntax.rb".freeze, "lib/hocon/config_util.rb".freeze, "lib/hocon/config_value.rb".freeze, "lib/hocon/config_value_factory.rb".freeze, "lib/hocon/config_value_type.rb".freeze, "lib/hocon/impl.rb".freeze, "lib/hocon/impl/abstract_config_node.rb".freeze, "lib/hocon/impl/abstract_config_node_value.rb".freeze, "lib/hocon/impl/abstract_config_object.rb".freeze, "lib/hocon/impl/abstract_config_value.rb".freeze, "lib/hocon/impl/array_iterator.rb".freeze, "lib/hocon/impl/config_boolean.rb".freeze, "lib/hocon/impl/config_concatenation.rb".freeze, "lib/hocon/impl/config_delayed_merge.rb".freeze, "lib/hocon/impl/config_delayed_merge_object.rb".freeze, "lib/hocon/impl/config_document_parser.rb".freeze, "lib/hocon/impl/config_double.rb".freeze, "lib/hocon/impl/config_impl.rb".freeze, "lib/hocon/impl/config_impl_util.rb".freeze, "lib/hocon/impl/config_include_kind.rb".freeze, "lib/hocon/impl/config_int.rb".freeze, "lib/hocon/impl/config_node_array.rb".freeze, "lib/hocon/impl/config_node_comment.rb".freeze, "lib/hocon/impl/config_node_complex_value.rb".freeze, "lib/hocon/impl/config_node_concatenation.rb".freeze, "lib/hocon/impl/config_node_field.rb".freeze, "lib/hocon/impl/config_node_include.rb".freeze, "lib/hocon/impl/config_node_object.rb".freeze, "lib/hocon/impl/config_node_path.rb".freeze, "lib/hocon/impl/config_node_root.rb".freeze, "lib/hocon/impl/config_node_simple_value.rb".freeze, "lib/hocon/impl/config_node_single_token.rb".freeze, "lib/hocon/impl/config_null.rb".freeze, "lib/hocon/impl/config_number.rb".freeze, "lib/hocon/impl/config_parser.rb".freeze, "lib/hocon/impl/config_reference.rb".freeze, "lib/hocon/impl/config_string.rb".freeze, "lib/hocon/impl/container.rb".freeze, "lib/hocon/impl/default_transformer.rb".freeze, "lib/hocon/impl/from_map_mode.rb".freeze, "lib/hocon/impl/full_includer.rb".freeze, "lib/hocon/impl/memo_key.rb".freeze, "lib/hocon/impl/mergeable_value.rb".freeze, "lib/hocon/impl/origin_type.rb".freeze, "lib/hocon/impl/parseable.rb".freeze, "lib/hocon/impl/path.rb".freeze, "lib/hocon/impl/path_builder.rb".freeze, "lib/hocon/impl/path_parser.rb".freeze, "lib/hocon/impl/replaceable_merge_stack.rb".freeze, "lib/hocon/impl/resolve_context.rb".freeze, "lib/hocon/impl/resolve_memos.rb".freeze, "lib/hocon/impl/resolve_result.rb".freeze, "lib/hocon/impl/resolve_source.rb".freeze, "lib/hocon/impl/resolve_status.rb".freeze, "lib/hocon/impl/simple_config.rb".freeze, "lib/hocon/impl/simple_config_document.rb".freeze, "lib/hocon/impl/simple_config_list.rb".freeze, "lib/hocon/impl/simple_config_object.rb".freeze, "lib/hocon/impl/simple_config_origin.rb".freeze, "lib/hocon/impl/simple_include_context.rb".freeze, "lib/hocon/impl/simple_includer.rb".freeze, "lib/hocon/impl/substitution_expression.rb".freeze, "lib/hocon/impl/token.rb".freeze, "lib/hocon/impl/token_type.rb".freeze, "lib/hocon/impl/tokenizer.rb".freeze, "lib/hocon/impl/tokens.rb".freeze, "lib/hocon/impl/unmergeable.rb".freeze, "lib/hocon/impl/unsupported_operation_error.rb".freeze, "lib/hocon/impl/url.rb".freeze, "lib/hocon/parser.rb".freeze, "lib/hocon/parser/config_document.rb".freeze, "lib/hocon/parser/config_document_factory.rb".freeze, "lib/hocon/parser/config_node.rb".freeze, "lib/hocon/version.rb".freeze] s.homepage = "https://github.com/puppetlabs/ruby-hocon".freeze s.licenses = ["Apache License, v2".freeze] s.required_ruby_version = Gem::Requirement.new(">= 1.9.0".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "HOCON Config Library".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, ["~> 1.5"]) s.add_development_dependency(%q.freeze, ["~> 2.14"]) else s.add_dependency(%q.freeze, ["~> 1.5"]) s.add_dependency(%q.freeze, ["~> 2.14"]) end else s.add_dependency(%q.freeze, ["~> 1.5"]) s.add_dependency(%q.freeze, ["~> 2.14"]) end end hocon-1.3.1/HISTORY.md0000644000004100000410000015344013704151031014337 0ustar www-datawww-data# enterprise_ruby-hocon_bump_and_tag_master - History ## Tags * [LATEST - 4 Apr, 2017 (b42a72f0)](#LATEST) * [1.2.4 - 3 Nov, 2016 (5157cc60)](#1.2.4) * [1.2.3 - 3 Nov, 2016 (cd9a5c8d)](#1.2.3) * [1.2.2 - 1 Nov, 2016 (4a29c034)](#1.2.2) * [1.2.1 - 27 Oct, 2016 (b6edea48)](#1.2.1) * [1.2.0 - 27 Oct, 2016 (1060d251)](#1.2.0) * [1.1.3 - 12 Oct, 2016 (bf4a7d4b)](#1.1.3) * [1.1.2 - 15 Jul, 2016 (6041a5c4)](#1.1.2) * [1.1.1 - 6 Jul, 2016 (5b2c8baa)](#1.1.1) * [1.1.0 - 1 Jul, 2016 (99b3145e)](#1.1.0) * [1.0.1 - 16 Mar, 2016 (aa36b692)](#1.0.1) * [1.0.0 - 16 Feb, 2016 (dc385fe2)](#1.0.0) * [0.9.3 - 14 Jul, 2015 (7defef59)](#0.9.3) * [0.9.2 - 30 Jun, 2015 (6b402bc2)](#0.9.2) * [0.9.1 - 30 Jun, 2015 (e8c2f405)](#0.9.1) * [0.9.0 - 10 Apr, 2015 (aeab6ab2)](#0.9.0) * [0.1.0 - 9 Apr, 2015 (bfdb7255)](#0.1.0) * [0.0.5 - 1 Oct, 2014 (67d264f4)](#0.0.5) * [0.0.3 - 24 Jul, 2014 (6cd552c3)](#0.0.3) * [0.0.2 - 24 Jul, 2014 (95dffaea)](#0.0.2) * [0.0.1 - 16 Mar, 2014 (f7dbca52)](#0.0.1) ## Details ### LATEST - 4 Apr, 2017 (b42a72f0) * (GEM) update hocon version to 1.2.5 (b42a72f0) * Merge pull request #108 from jpinsonault/maint-prepare-for-1.2.5 (50b0087b) ``` Merge pull request #108 from jpinsonault/maint-prepare-for-1.2.5 (MAINT) Change version back 1.2.5.SNAPSHOT ``` * (MAINT) Change version back 1.2.5.SNAPSHOT (40d45c77) ``` (MAINT) Change version back 1.2.5.SNAPSHOT CI needs the current to be less than the next version to be released ``` * Merge pull request #107 from jpinsonault/PE-18165-support-for-utf8-file-paths (c65941f2) ``` Merge pull request #107 from jpinsonault/PE-18165-support-for-utf8-file-paths (PE-18165) Support for utf-8 file paths ``` * (MAINT) Prepare for 1.2.5 release (8edf0841) ``` (MAINT) Prepare for 1.2.5 release Update changelog and version ``` * (PE-18165) Support for utf-8 file paths (a54c93b5) ``` (PE-18165) Support for utf-8 file paths This commit removes the dependency on Addressable and adds some comments regarding some screwy areas in the code where we half heartedly tried to support loading of URIs It maintains utf-8 file support ``` * Merge pull request #105 from mwbutcher/maint/master/PE-18165_encode_file_URIs_in_order_to_handle_utf-8_chars (e2725955) ``` Merge pull request #105 from mwbutcher/maint/master/PE-18165_encode_file_URIs_in_order_to_handle_utf-8_chars (PE-18165) encode file URIs to handle utf8 chars ``` * (PE-18165) encode file URIs to handle utf8 chars (51525f43) ``` (PE-18165) encode file URIs to handle utf8 chars Prior to this change, the hocon parser would error when give file names like ᚠᛇᚻ.conf or /tmp/旗本/pe.conf. This commit URI encodes the filenames to avoid that issue. ``` * Merge pull request #104 from puppetlabs/rm_cprice404 (ddb4afb2) ``` Merge pull request #104 from puppetlabs/rm_cprice404 remove cprice404 ``` * remove cprice404 (f74fb2ca) * Merge pull request #98 from puppetlabs/theshanx-patch-1 (c532a69e) ``` Merge pull request #98 from puppetlabs/theshanx-patch-1 (maint) Add internal_list key to MAINTAINERS ``` * Merge pull request #102 from jpinsonault/maint-fix-typo-in-readme (ecd2de47) ``` Merge pull request #102 from jpinsonault/maint-fix-typo-in-readme (MAINT) Fix typo in readme ``` * (MAINT) Fix typo in readme (10961a98) * Merge pull request #101 from jpinsonault/maint-update-changelog-after-1.2.4-release (c8d543ad) ``` Merge pull request #101 from jpinsonault/maint-update-changelog-after-1.2.4-release (MAINT) Update changelog for 1.2.4 ``` * (MAINT) Update changelog for 1.2.4 (c7a5edf1) ``` (MAINT) Update changelog for 1.2.4 And explain missing version numbers ``` * (maint) Add internal_list key to MAINTAINERS (e327d214) ``` (maint) Add internal_list key to MAINTAINERS This change adds a reference to the Google group the maintainers are associated with. ``` ### 1.2.4 - 3 Nov, 2016 (5157cc60) * (HISTORY) update ruby-hocon history for gem release 1.2.4 (5157cc60) * (GEM) update hocon version to 1.2.4 (67ff0795) * Merge pull request #100 from jpinsonault/maint-update-version-to-1.2.4 (3a493130) ``` Merge pull request #100 from jpinsonault/maint-update-version-to-1.2.4 (MAINT) Update version to 1.2.4 ``` * (MAINT) Update version to 1.2.4 (958326d4) ### 1.2.3 - 3 Nov, 2016 (cd9a5c8d) * (HISTORY) update ruby-hocon history for gem release 1.2.3 (cd9a5c8d) * (GEM) update hocon version to 1.2.3 (f2f3e235) * Merge pull request #99 from jpinsonault/maint-update-version-to-1.2.3 (18324c6d) ``` Merge pull request #99 from jpinsonault/maint-update-version-to-1.2.3 (MAINT) Update version 1.2.3 ``` * (MAINT) Update version 1.2.3 (e7be1d78) ### 1.2.2 - 1 Nov, 2016 (4a29c034) * (HISTORY) update ruby-hocon history for gem release 1.2.2 (4a29c034) * (GEM) update hocon version to 1.2.2 (5cf6b037) * Merge pull request #97 from jpinsonault/maint-update-version-for-release (e973ee34) ``` Merge pull request #97 from jpinsonault/maint-update-version-for-release (MAINT) Update version for release ``` * (MAINT) Update version for release (e81fecf9) ### 1.2.1 - 27 Oct, 2016 (b6edea48) * (HISTORY) update ruby-hocon history for gem release 1.2.1 (b6edea48) * (GEM) update hocon version to 1.2.1 (0e06af2f) * Merge pull request #96 from jpinsonault/maint-update-version-to-1.2.1.SNAPSHOT (418d5e24) ``` Merge pull request #96 from jpinsonault/maint-update-version-to-1.2.1.SNAPSHOT (MAINT) Update version to 1.2.1.SNAPSHOT ``` * (MAINT) Update version to 1.2.1.SNAPSHOT (20d34a33) ### 1.2.0 - 27 Oct, 2016 (1060d251) * (HISTORY) update ruby-hocon history for gem release 1.2.0 (1060d251) * (GEM) update hocon version to 1.2.0 (33a9edef) * Merge pull request #95 from jpinsonault/maint-fix-pre-release-version-string (dba994b3) ``` Merge pull request #95 from jpinsonault/maint-fix-pre-release-version-string (MAINT) Fix version string ``` * (MAINT) Fix version string (622fb2ab) * Merge pull request #94 from jpinsonault/maint-update-release-date (41673be4) ``` Merge pull request #94 from jpinsonault/maint-update-release-date (MAINT) Update date in gemfile for 1.2.0 release ``` * (MAINT) Update date in gemfile for 1.2.0 release (25e834fc) * Merge pull request #93 from jpinsonault/maint-prepare-for-1.2.0-release (c0ab30b1) ``` Merge pull request #93 from jpinsonault/maint-prepare-for-1.2.0-release (MAINT) Update version and changelog for 1.2.0 release ``` * (MAINT) Update version and changelog for 1.2.0 release (d4ac81ac) * Merge pull request #92 from jpinsonault/maint-revert-moving-version.rb (bd5a065e) ``` Merge pull request #92 from jpinsonault/maint-revert-moving-version.rb Revert "(MAINT) Move version.rb to work with ci" ``` * Revert "(MAINT) Move version.rb to work with ci" (a17015fc) ``` Revert "(MAINT) Move version.rb to work with ci" This reverts commit 5be440a433141e7dab5534d2309282d0865adca7. ``` * Merge pull request #90 from puppetlabs/add-issue-tracker-link (b046a116) ``` Merge pull request #90 from puppetlabs/add-issue-tracker-link Include link to Jira issue tracker in README ``` * Merge pull request #91 from jpinsonault/maint-move-version.rb-for-ci (dd9753d2) ``` Merge pull request #91 from jpinsonault/maint-move-version.rb-for-ci (MAINT) Move version.rb to work with ci ``` * (MAINT) Move version.rb to work with ci (5be440a4) ``` (MAINT) Move version.rb to work with ci Jenkins expects the version.rb file to be under lib//version.rb ``` * Include link to Jira issue tracker in README (75609de4) * Merge pull request #86 from jpinsonault/hc-92-add-cli-tool-for-hocon (ea0ddcae) ``` Merge pull request #86 from jpinsonault/hc-92-add-cli-tool-for-hocon [WIP] (HC-92) Add cli tool for hocon ``` * (HC-92) Have unset throw an error on missing paths (2577e8fe) ``` (HC-92) Have unset throw an error on missing paths Refactor the way errors are handled. Rather than catching the hocon parser errors, we now raise our own error to make things clearer Add tests for new exception ``` * (HC-92) Remove flock calls (14c548b5) * (MAINT) Update CHANGELOG for 1.1.3 (f02b161a) * (HC-92) Fix version require for ruby 1.9 (d21fb360) * (HC-92) Move version string to Hocon::Version module (ca9de704) * (HC-92) Add -f option, update docs (afe75db5) * (HC-92) Lock files while reading/writing (6271c25e) * (HC-92) Update readme with CLI docs (1afa6c2c) * (HC-92) Update optparse banner with more info (2a047009) * (HC-92) Make new render option optional (06429cac) * (HC-92) Add tests for new render option (2762f01f) ``` (HC-92) Add tests for new render option Adds tests for key_value_separator render option ``` * (HC-92) Add cli tests for setting complex types (3d548f33) * (HC-92) Add key_value_separator render option (b12a822c) ``` (HC-92) Add key_value_separator render option Also updates the CLI tool to use the colon separator in the set subcommand ``` * (HC-92) Update version to 1.2.0 (4746078e) * (HC-92) Whitespace (b9b65fd5) * (HC-92) Remove default space before colons in maps (12a31ca6) * (HC-92) Add spec tests for cli functions (8b06d660) * (HC-92) Improve modularity (846040d0) * (HC-92) Add json output support (6a203bf3) * (HC-92) Better error handling (88a6abca) * (HC-92) Add --out-file support (07bdef54) * (HC-92) Move cli code to lib dir (fb0483fc) * (HC-92) Add STDIN support (61b872c5) * (HC-92) Add CLI tool for hocon (21d5f01d) ### 1.1.3 - 12 Oct, 2016 (bf4a7d4b) * Merge pull request #88 from cprice404/bug/master/parse-with-substitutions (bf4a7d4b) ``` Merge pull request #88 from cprice404/bug/master/parse-with-substitutions Fix bug in `Hocon.parse` with substitutions ``` * Fix bug in `Hocon.parse` with substitutions (07b265b9) ``` Fix bug in `Hocon.parse` with substitutions Currently, if you call `Hocon.parse` with a string that contains substitutions, you will get a `ConfigNotResolvedError`. We had this issue with `Hocon.load` earlier, and modified it to include the code necessary to resolve the config object before returning it. However, we didn't make the same changes for `parse`, so the behavior actually diverged between the two. This commit fixes up `parse` in the same way that we previously fixed up `load`. ``` * Merge pull request #85 from cprice404/maint/master/200-add-maintainers (d98ad200) ``` Merge pull request #85 from cprice404/maint/master/200-add-maintainers (200) Add MAINTAINERS ``` * (200) Add MAINTAINERS (8119f366) * Merge pull request #84 from jpinsonault/maint-add-ruby-gems-widget (a8318435) ``` Merge pull request #84 from jpinsonault/maint-add-ruby-gems-widget (MAINT) Add rubygems version widget ``` * (MAINT) Add rubygems version widget (3b9bb37e) ### 1.1.2 - 15 Jul, 2016 (6041a5c4) * Merge pull request #83 from jpinsonault/maint-update-changelog-for-1.1.2 (6041a5c4) ``` Merge pull request #83 from jpinsonault/maint-update-changelog-for-1.1.2 (MAINT) Update changelog/version for 1.1.2 release ``` * (MAINT) Update changelog/version for 1.1.2 release (947e6aa4) * Merge pull request #82 from Iristyle/ticket/master/HC-82-parse-files-as-utf8-with-boms (a58adc87) ``` Merge pull request #82 from Iristyle/ticket/master/HC-82-parse-files-as-utf8-with-boms (HC-82) Enable UTF-8 with BOM parsing ``` * (HC-82) Add spec for UTF-8 filenames (b0d702c1) ``` (HC-82) Add spec for UTF-8 filenames - Hocon does not currently handle UTF-8 filenames properly ``` * (HC-82) Remove invalid BOM spec (9a5cabea) ``` (HC-82) Remove invalid BOM spec - A skipped test exists for validating a string can be passed to parse_string that starts with a UTF8 BOM \uFEFF - However, when comparing this to the Ruby JSON parser, that parser also doesn't handle this seemingly edge case. Arguably, by the time a string read from a file is passed to a parsing engine, it will be in the correct encoding, and will have leading BOMs trimmed off. For reference, Ruby JSON behavior: [1] pry(main)> require 'json' => true [2] pry(main)> json = "\uFEFF{ \"foo\": \"bar\" }" => "{ \"foo\": \"bar\" }" [3] pry(main)> JSON.parse(json) JSON::ParserError: 757: unexpected token at '{ "foo": "bar" }' from /usr/local/opt/rbenv/versions/2.1.9/lib/ruby/2.1.0/json/common.rb:155:in `parse' ``` * (HC-82) Add additional file encoding specs (9e04970e) ``` (HC-82) Add additional file encoding specs - Show that UTF-8 content is properly handled - Show that UTF-16 content is not yet supported ``` * (HC-82) Enable UTF-8 with BOM parsing (8ccf6625) ``` (HC-82) Enable UTF-8 with BOM parsing - Previously Hocon::ConfigFactory.parse_file did not specify an encoding, and didn't allow for files with UTF-8 BOMs on Windows. In reality, HOCON config files should be detected by their BOM and treated as UTF-8, UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE based on the presence of the BOM, with a fallback to UTF-8 when one is not present, based on RFC 4627 at https://www.ietf.org/rfc/rfc4627.txt This fix is a bit naive as it may improperly load HOCON config files on Windows which are UCS-2 (a precursor to UTF-16LE). Its recommended that this be addressed later in a better File parsing scheme that peeks at the first few bytes of the file to determine the encoding correctly. ``` ### 1.1.1 - 6 Jul, 2016 (5b2c8baa) * Merge pull request #81 from janelu2/master (5b2c8baa) ``` Merge pull request #81 from janelu2/master (MAINT) update CHANGELOG.md and version number for z release of 1.1.1 ``` * (MAINT) update CHANGELOG.md and version number for z release of 1.1.1 (582cd7e2) * Merge pull request #80 from janelu2/master (17ecc8d4) ``` Merge pull request #80 from janelu2/master (HC-81) Fix undefined method `value_type_name' error ``` * (HC-81) Add tests and fix value_type_name calls (466ca82a) ``` (HC-81) Add tests and fix value_type_name calls (MAINT) fix test to correctly call the wrong error (MAINT) add require to config_value_type and use alias ``` ### 1.1.0 - 1 Jul, 2016 (99b3145e) * Merge pull request #78 from jpinsonault/maint-update-changelog-for-release (99b3145e) ``` Merge pull request #78 from jpinsonault/maint-update-changelog-for-release (MAINT) Update changelog and version for release ``` * (MAINT) Fix changelog version (aa3fef17) * Merge pull request #79 from janelu2/master (17d96e4b) ``` Merge pull request #79 from janelu2/master (MAINT) update readme ``` * (MAINT) update readme (3123b670) * (MAINT) Update changelog and version for release (213e10f8) ``` (MAINT) Update changelog and version for release Update changelog Bump version to 1.1.0 Update gitignore with Gemfile.lock ``` * Merge pull request #77 from cprice404/bug/master/HC-80-dont-shadow-ruby-class-module-name (61194207) ``` Merge pull request #77 from cprice404/bug/master/HC-80-dont-shadow-ruby-class-module-name (HC-80) Don't shadow ruby Class/Module#name method ``` * Merge pull request #76 from cprice404/maint/master/HC-79-support-format-arg-in-load (c0c88698) ``` Merge pull request #76 from cprice404/maint/master/HC-79-support-format-arg-in-load (HC-79) support :syntax arg in load ``` * (MAINT) Change variable name for readability (cd3505ed) * (HC-80) Don't shadow ruby Class/Module#name method (4f7c8ccc) ``` (HC-80) Don't shadow ruby Class/Module#name method Prior to this commit, there were a few places in the code where we'd ported over a class-or-module-level method named `name` from the upstream library. This isn't a good idea in Ruby because it results in shadowing of the built in Ruby methods Class#name and Module#name. This was causing problems for some users, e.g. in cases where reflection is being used to examine classes. In this commit we rename all such methods to something more specific, and replace the calls to the old names with calls to the new names. ``` * (HC-79) support `:syntax` option in simple `load` (a5663ca6) ``` (HC-79) support `:syntax` option in simple `load` This commit adds support for an optional `opts` map to be passed in to the simple `load` method. If provided, this map may contain a `:syntax` key that explicitly specifies which config format/syntax the user expects the file to be in. This provides a way for users to load files whose file extension doesn't match the built-in expectations for which file extensions use which syntaxes. The commit also provides some error checking for the case where an explicit `:syntax` is not passed in, and the file extension isn't recognized. In this case, we will throw an error now, rather than silently returning an empty map like we did in the past. Finally, this commit adds some notes to the docs/example usage, indicating how to pass in an explicit syntax. ``` * (MAINT) separate ruby tests from upstream tests (3cd0f049) * Merge pull request #74 from karenvdv/server-1300-add-maintainers (ffc0e143) ``` Merge pull request #74 from karenvdv/server-1300-add-maintainers Add maintainers section ``` * Add maintainers section (6fec1cdc) ### 1.0.1 - 16 Mar, 2016 (aa36b692) * Update for 1.0.1 release (aa36b692) * Update date for 1.0.0 release (9cbb6175) ### 1.0.0 - 16 Feb, 2016 (dc385fe2) * Merge pull request #72 from jpinsonault/maint-bump-version-to-1.0.0 (dc385fe2) ``` Merge pull request #72 from jpinsonault/maint-bump-version-to-1.0.0 (MAINT) bump and relabel version 0.9.4 to 1.0.0 ``` * (MAINT) Add link to readme (d6312759) * (MAINT) Update gemfile.lock (a9b721b6) * (MAINT) Whitespace - Cleanup README (920ffafb) * (MAINT) Bump and relabel 0.9.4 to 1.0.0 (e129b2ee) ``` (MAINT) Bump and relabel 0.9.4 to 1.0.0 0.9.4 introduced changes that require some users to modify their require statements due to bugfixes. In addition the API is stable enough to consider this a 1.0.0 release 1.0.0 Changlog: This is a bugfix release. The API is stable enough and the code is being used in production, so the version is also being bumped to 1.0.0 * Fixed a bug wherein calling "Hocon.load" would not resolve substitutions. * Fixed a circular dependency between the Hocon and Hocon::ConfigFactory namespaces. Using the Hocon::ConfigFactory class now requires you to use a `require 'hocon/config_factory'` instead of `require hocon` * Add support for hashes with keyword keys ``` * Merge pull request #71 from fpringvaldsen/maint/changelog (2b55e3b7) ``` Merge pull request #71 from fpringvaldsen/maint/changelog Fix changelog for 0.9.4 ``` * Fix changelog for 0.9.4 (7e417107) ``` Fix changelog for 0.9.4 Add changes that went into the 0.9.4 release that weren't listed in the changelog. ``` * (MAINT) Update Gemfile.lock (d9f1d4c8) * (MAINT) Update for 0.9.4 release (ec909659) * Merge pull request #70 from fpringvaldsen/maint/load-issue (1ffa268a) ``` Merge pull request #70 from fpringvaldsen/maint/load-issue (MAINT) Fix Hocon.load substitution issue ``` * (MAINT) Fix Hocon.load substitution issue (31321f0b) ``` (MAINT) Fix Hocon.load substitution issue Previously, Hocon.load was calling into the ConfigFactory.parse_file method. However, the `parse` methods in ConfigFactory do not resolve substitutions, as that is the intent of the `load` methods. This commit updates the Hocon.load method to call into ConfigFactory.load_file. It also updates the readme to explain the usage of ConfigDocuments, and adds a comment to explain that the `load` methods in ConfigFactory should be used if substitutions are present ``` * Merge pull request #68 from krjackso/master (f9f29a3f) ``` Merge pull request #68 from krjackso/master Fix typo when referencing GENERIC OriginType ``` * Fix typo when referencing GENERIC OriginType (0d1a65fc) * Merge pull request #66 from traylenator/addspec (c9f56235) ``` Merge pull request #66 from traylenator/addspec Add spec tests to gem file. Fixes #65 ``` * Add spec tests to gem file. Fixes #65 (573da794) * Merge pull request #64 from cprice404/maint/master/fix-circular-deps (618592a1) ``` Merge pull request #64 from cprice404/maint/master/fix-circular-deps (HC-24) Use simple API in README, fix circular deps ``` * (HC-24) Use simple API in README, fix circular deps (3783f305) ``` (HC-24) Use simple API in README, fix circular deps This commit updates the README to show the simpler version of the API for basic read operations. It also fixes some circular dependencies that were causing the example code for the ConfigDocumentFactory not to work properly. ``` * Merge pull request #62 from fpringvaldsen/improvement/TK-251/keyword-keys (0e7216c4) ``` Merge pull request #62 from fpringvaldsen/improvement/TK-251/keyword-keys (TK-251) Convert symbol keys to strings ``` * (TK-251) Process nested hashes (724f792d) ``` (TK-251) Process nested hashes When converting a Hashes symbol keys to strings, also process any nested hashes that are present. ``` * (TK-251) Convert symbol keys to strings (89f57cc1) ``` (TK-251) Convert symbol keys to strings When parsing a Hash in ConfigValueFactory, automatically convert all symbol keys to strings. ``` ### 0.9.3 - 14 Jul, 2015 (7defef59) * Update Changelog and Gemspec for 0.9.3 (7defef59) * Merge pull request #61 from fpringvaldsen/bug/TK-249/bad-comments (be17f2a4) ``` Merge pull request #61 from fpringvaldsen/bug/TK-249/bad-comments (TK-249) Remove unnecessary comments in output ``` * (TK-249) Remove unnecessary comments in output (e893d6fc) ``` (TK-249) Remove unnecessary comments in output Remove unnecessary "# hardcoded value" comments that were being generated when inserting a hash or an array into a ConfigDocument. ``` ### 0.9.2 - 30 Jun, 2015 (6b402bc2) * Update CHANGELOG and gemspec for 0.9.2 (6b402bc2) * Merge pull request #59 from fpringvaldsen/maint/undefined-method-fix (96499050) ``` Merge pull request #59 from fpringvaldsen/maint/undefined-method-fix (MAINT) Fix undefined method bug ``` * (MAINT) Fix undefined method bug (a47ee9b0) ``` (MAINT) Fix undefined method bug Fix an undefined method bug that was occurring when attempting to add a complex value into an empty root object. ``` ### 0.9.1 - 30 Jun, 2015 (e8c2f405) * Update CHANGELOG and gemspec for 0.9.1 (e8c2f405) * Merge pull request #58 from fpringvaldsen/bug/TK-246/single-line-config (c4bfc3c0) ``` Merge pull request #58 from fpringvaldsen/bug/TK-246/single-line-config (TK-246) Fix single-line config bug ``` * (TK-246) Fix single-line config bug (9305707b) ``` (TK-246) Fix single-line config bug Previously there was a bug wherein building out a config starting from an empty ConfigDocument would cause the entire config to exist on a single line. Fix this bug by modifying the addition of new maps along a path to add multi-line maps instead of single-line maps if the object being added to is an empty root or a multi-line object. ``` * Merge pull request #57 from cprice404/maint/master/improve-error-messages-for-problem-tokens (afeed2a0) ``` Merge pull request #57 from cprice404/maint/master/improve-error-messages-for-problem-tokens (MAINT) Improve error messages for Problem tokens ``` * (MAINT) Improve error messages for Problem tokens (be4a9320) ``` (MAINT) Improve error messages for Problem tokens Prior to this commit, the `Problem` token type called `to_s` on an internal `StringIO` object when building up an error string to return to the user. Calling `to_s` on a `StringIO` just causes it to print out, basically, `#`, so you don't get the useful error message. This patch changes the code to call `string` instead, which returns a much more useful error message. ``` ### 0.9.0 - 10 Apr, 2015 (aeab6ab2) * Merge pull request #56 from jpinsonault/maint-update-for-0.9.0-release (aeab6ab2) ``` Merge pull request #56 from jpinsonault/maint-update-for-0.9.0-release (MAINT) Update for 0.9.0 release ``` * (MAINT) Update for 0.9.0 release (a139e789) * Merge pull request #55 from fpringvaldsen/maint/empty-doc-test (5b7b7d8f) ``` Merge pull request #55 from fpringvaldsen/maint/empty-doc-test (MAINT) Add empty document insertion test ``` * (MAINT) Add additional ConfigValue insertion test (9d307477) ``` (MAINT) Add additional ConfigValue insertion test Add an additional test for inserting a ConfigValue into a ConfigDocument. Fix an issue wherein this would fail as the rendered result of ConfigValue was not having whitespace trimmed. ``` * (MAINT) Add empty document insertion test (62394f9c) ``` (MAINT) Add empty document insertion test Add a test for insertion into an empty ConfigDocument. ``` ### 0.1.0 - 9 Apr, 2015 (bfdb7255) * (MAINT) Update gemspec for 0.1.0 release (bfdb7255) * (MAINT) Remove SimpleConfigDocument require (bea56c92) ``` (MAINT) Remove SimpleConfigDocument require Remove the SimpleConfigDocument require from the ConfigDocument spec, as this was causing an issue wherein Parseable would work properly even though it needed to require SimpleConfigDocument. ``` * (MAINT) Fix uninitialized constant error (fd2abd12) * Merge pull request #53 from fpringvaldsen/task/TK-188/refactor-parser (30c5feee) ``` Merge pull request #53 from fpringvaldsen/task/TK-188/refactor-parser (TK-188) Refactor Parser ``` * Merge pull request #54 from jpinsonault/tk-161-port-public-api-tests (0a4fcbe0) ``` Merge pull request #54 from jpinsonault/tk-161-port-public-api-tests (TK-161) port public api tests ``` * Added test for load_file_with_resolve_options (7afc6053) * Addressed PR feedback (029db783) * Merge pull request #52 from fpringvaldsen/task/TK-187/port-ConfigDocument (73471f1b) ``` Merge pull request #52 from fpringvaldsen/task/TK-187/port-ConfigDocument (TK-187) Port ConfigDocument and tests ``` * (MAINT) Fix typos (f3102ef8) ``` (MAINT) Fix typos Fix typos in comment and test string. ``` * (MAINT) Fix failing ConfigValue test (9ddbc290) ``` (MAINT) Fix failing ConfigValue test Fix bug with the rendering of SimpleConfigList that was causing a skipped ConfigValue test to fail. ``` * Refactor Parser (fdc74366) ``` Refactor Parser Refactor the Parser class into ConfigParser, and change it to parse ConfigNodes rather than Tokens. Change Parseable to first parse a ConfigDocument, then use that to parse a Config. ``` * (MAINT) Clean-up loops (5d10c6fc) ``` (MAINT) Clean-up loops Clean up certain loops in ConfigNode and ConfigDocument implementations to be more ruby-esque. ``` * (TK-187) Port ConfigDocument tests (df22b9f7) ``` (TK-187) Port ConfigDocument tests Port all ConfigDocument tests down to ruby-hocon and get them passing. ``` * Merge pull request #51 from fpringvaldsen/task/TK-186/port-ConfigNode (63c8907a) ``` Merge pull request #51 from fpringvaldsen/task/TK-186/port-ConfigNode (TK-186) Port ConfigNode tests ``` * (TK-187) Port ConfigDocument classes/interfaces (1b8f5eea) ``` (TK-187) Port ConfigDocument classes/interfaces Port the ConfigDocument classes and interfaces from the upstream library, sans tests. ``` * (TK-186) Update comment on AbstractConfigNodeValue (391d1c89) ``` (TK-186) Update comment on AbstractConfigNodeValue Update the comment on the AbstractConfigNodeValue to reflect that the module is unnecessary in Ruby and is being preserved solely for consistency. ``` * (TK-186) Make abstract classes into modules (00a7d5dd) ``` (TK-186) Make abstract classes into modules Change all ConfigNode classes that are abstract in the upstream library into modules. Change the ConfigNode class to a module. ``` * (TK-186) Fix typo in comment_text method name (73da7db5) ``` (TK-186) Fix typo in comment_text method name Change the commentText method to comment_text. ``` * (TK-187) Port ConfigDocumentParser tests (19134ddd) ``` (TK-187) Port ConfigDocumentParser tests Port all tests for ConfigDocumentParser and ensure they are passing. ``` * (TK-187) Port ConfigDocumentParser (18f55f46) ``` (TK-187) Port ConfigDocumentParser Port the ConfigDocumentParser class (sans tests) from the upstream library. ``` * (TK-161) Port Public API tests to ruby-hocon (03bc79a1) * (MAINT) Fix issue with concatenation tests (14615490) * (TK-186) Port ConfigNode tests (f5197a2a) ``` (TK-186) Port ConfigNode tests Port all the ConfigNode tests in the upstream library. Make various bugfixes to get the tests passing. ``` * Merge pull request #50 from KevinCorcoran/errmagerhd (798ab05a) ``` Merge pull request #50 from KevinCorcoran/errmagerhd (TK-162) enable concatenation test cases + fixes ``` * (TK-186) Implement ConfigNode classes (3600a2be) ``` (TK-186) Implement ConfigNode classes Implement all the various ConfigNode classes from the upstream library. ``` * Merge pull request #48 from jpinsonault/tk-159-round-three-config-value-tests (5e95a622) ``` Merge pull request #48 from jpinsonault/tk-159-round-three-config-value-tests (TK-159) Final round of config value tests ``` * Addressed PR feedback (d5c04edc) * Merge pull request #49 from cprice404/maint/master/excepton-typo (7d31e86f) ``` Merge pull request #49 from cprice404/maint/master/excepton-typo (MAINT) fix 'excepton' typo ``` * (MAINT) fix 'excepton' typo (2f705314) * Merge pull request #47 from cprice404/feature/master/TK-160-more-conf-parser-tests (e73f00fb) ``` Merge pull request #47 from cprice404/feature/master/TK-160-more-conf-parser-tests (TK-160) More config parser tests ``` * Addressed PR feedback (a5e314da) * (TK-160) Improve comments re: BOM tests (a054355b) * Merge pull request #46 from KevinCorcoran/delayed-merge (c2a58a41) ``` Merge pull request #46 from KevinCorcoran/delayed-merge implement rest of CDMO + other bugfixes ``` * (TK-160) Finished implementing conf parser tests (d19ea3bc) * (TK-160) Port multi-field comment tests (c959e2c9) * (TK-160) Port comment tests (dbea64ae) * (TK-160) Port more conf parser tests (07a3f335) * Merge pull request #45 from cprice404/feature/master/TK-160-more-include-parser-tests (c3b622e0) ``` Merge pull request #45 from cprice404/feature/master/TK-160-more-include-parser-tests (TK-160) fix remaining "valid conf" parser tests ``` * (TK-160) Fix typos (ac6f9c44) * (TK-162) enable concatenation test cases + fixes (de643885) ``` (TK-162) enable concatenation test cases + fixes Un-comment the remaining concatenation test cases that were still commented-out and fix bugs. Also added 'inspect' implementations and use short class names to make trace output match upstream, and re-wrote various bits of code to correspond more closely to upstream. ``` * (maint) port rest of MemoKey and fix a couple bugs (7374d543) * (maint) sync SimpleConfigObject.== with upstream (3f4d7fe0) * (maint) sync ConfigDelayedMergeObject w/ upstream (df402953) * (TK-159) Final round of config value tests (cfdaa58d) ``` (TK-159) Final round of config value tests Implemented AbstractConfigValue#at_path/at_key ``` * Merge pull request #44 from KevinCorcoran/finish-concat-test-2 (a01c2214) ``` Merge pull request #44 from KevinCorcoran/finish-concat-test-2 (TK-162) concat tests ``` * (maint) add comment about Ruby vs. Java integers (34fca36e) * (TK-160) More config parser tests (b3b48fd0) * (MAINT) Remove code related to '.properties' files (69367ad0) * (TK-160) Get `include` "valid conf" parser tests passing (bea8a481) * (TK-162) comment-out failing concat test cases (2fdb2621) * Merge pull request #43 from cprice404/feature/master/TK-160-more-valid-conf-parser-tests (1da67e76) ``` Merge pull request #43 from cprice404/feature/master/TK-160-more-valid-conf-parser-tests (TK-160) more valid conf parser tests ``` * (TK-160) re-enable += tests, they are passing now (fd646c39) * Merge pull request #40 from cprice404/maint/master/re-sync-parser-and-tokenizer (903c9bdd) ``` Merge pull request #40 from cprice404/maint/master/re-sync-parser-and-tokenizer (MAINT) Update Parser to match latest upstream ``` * (MAINT) Fix bugs and port ConfigNode interface (efcbacea) * Merge pull request #42 from KevinCorcoran/config-string (710b3f80) ``` Merge pull request #42 from KevinCorcoran/config-string (maint) sync ConfigString with upstream ``` * (MAINT) Sync ConfigDelayedMerge (4e389d3b) * (MAINT) re-sync `Path` class (853b447a) * Merge pull request #41 from KevinCorcoran/fix-null-in-concat (146f22bf) ``` Merge pull request #41 from KevinCorcoran/fix-null-in-concat Fix null in concat ``` * (maint) sync ConfigString with upstream (e13ea5a7) ``` (maint) sync ConfigString with upstream Also, replace calls to ConfigString.new with Quoted/Unquoted. ``` * (TK-162) finish porting concatenation tests (7a6f8d75) * Merge pull request #39 from KevinCorcoran/resolve-source-and-concat-test (b1874e34) ``` Merge pull request #39 from KevinCorcoran/resolve-source-and-concat-test port ResolveSource and concat test case ``` * (maint) port concatenation tests and fix bugs (f62a49c8) * (maint) small refactor to match upstream (6d2474df) ``` (maint) small refactor to match upstream Re-write a few bits of SimpleConfigObject.render_value_to_sb to make it more closely match the upstream version. ``` * (maint) fix bad reference to self.class (f77a983d) * Merge pull request #36 from KevinCorcoran/sync-up-config-concat (6ac66904) ``` Merge pull request #36 from KevinCorcoran/sync-up-config-concat (maint) sync ConfigConcatenation with upstream ``` * (maint) fix typo and log message (f33e5b31) * Merge pull request #37 from jpinsonault/tk-159-round-two-config-value-tests (9c6b2333) ``` Merge pull request #37 from jpinsonault/tk-159-round-two-config-value-tests (TK-159) Round Two of ConfigValue tests ``` * (maint) use self.class instead of class name (d5c8046b) * (maint) fix method name to match upstream (8a5b1b12) * Merge pull request #35 from cprice404/maint/master/flesh-out-simple-config-list (517ac3a3) ``` Merge pull request #35 from cprice404/maint/master/flesh-out-simple-config-list (MAINT) Flesh out SimpleConfigList ``` * Addressed PR feedback (a142e824) * (MAINT) Fix immutable exception type, bugs in SCOrigin (c1696790) ``` (MAINT) Fix immutable exception type, bugs in SCOrigin This commit does the following: * Changes the exception type for the `we_are_immutable` cases to use a new `UnsupportedOperationError`, to make the behavior model the Java version more closely. * Fix a couple of bugs in the ==/hash methods of SimpleConfigOrigin ``` * (MAINT) Update Parser to match latest upstream (f4b4ee62) * (TK-162) additional concatenation test case (2650e4b2) ``` (TK-162) additional concatenation test case ... and the changes to the production code required for it to pass. ``` * (maint) sync ResolveSource with upstream version (153c91e3) * (MAINT) Flesh out SimpleConfigOrigin (8179f4e5) * Merge pull request #34 from cprice404/maint/master/flesh-out-simple-config-object (1b4976c4) ``` Merge pull request #34 from cprice404/maint/master/flesh-out-simple-config-object (MAINT) flesh out simple config object ``` * (MAINT) Flesh out SimpleConfigList (35ec6306) * (maint) sync ConfigConcatenation with upstream (ef2dbdff) * (MAINT) Change `RuntimeError` to `ConfigError`. (e805e83c) * (TK-160) Get most 'valid conf' parser tests passing (7c93a5b3) ``` (TK-160) Get most 'valid conf' parser tests passing This commit fixes a ton of bugs and syncs some classes necessary to get most of the 'valid conf' parser tests passing. ``` * (MAINT) Another fix to a bad line in SCO (3b638f5c) * (MAINT) fix bad line of port of SimpleConfigObject (c24850e3) * (TK-160) Add `it` block for test counts (af7cfc2e) ``` (TK-160) Add `it` block for test counts Adding this `it` block causes rspec to correctly update the test counts based on these invalid configuration parsing tests. ``` * (MAINT) Finish porting / clean up AbstractConfigObject (bad5d034) * Merge pull request #33 from cprice404/feature/master/TK-160-port-conf-parser-tests (f980ff46) ``` Merge pull request #33 from cprice404/feature/master/TK-160-port-conf-parser-tests (TK-160) Port minimal `include` functionality ``` * (TK-160) fix whitespace, add cause to MalformedUrlError (0d9afb51) * (MAINT) Finish porting / clean up AbstractConfigValue (ee244518) * (MAINT) Finish porting / clean up SimpleConfigObject (659bbcda) * (TK-160) Fix config parse tests related to config_reference (d7f35022) * Merge pull request #32 from jpinsonault/tk-159-partial-set-of-config-value-tests (82648f63) ``` Merge pull request #32 from jpinsonault/tk-159-partial-set-of-config-value-tests (TK-159) Partial set of ConfigValue tests implemented ``` * Added another include_all? test, fixed description typo (60c5f83a) * (TK-160) Got most of the `reference` tests passing. (4ce36feb) * (TK-160) Port minimal `include` functionality (04989412) ``` (TK-160) Port minimal `include` functionality This commit re-enables some disabled config parser tests that had been failing due to missing functionality around HOCON's `include` capabilities. It also includes a minimal port of all of the `include` functionality that was required to get the tests passing. ``` * Addressed PR comments (16ccac67) ``` Addressed PR comments Implemented and added test for SimpleConfigList#include_all? Made ConfigReference#not_resolved private and not static Fixed typo in ConfigReference#relativized ConfigDelayedMergeObject#unwrapped now throws not_resolved Made various methods private to match Java version SimpleConfigObject#map_equals: got rid of confusing ugly lambda, uses sorted keys now No longer flay the ConfigDelayedMerge objects ``` * Using self in test_utils (efa3151b) * (TK-159) More ConfigValue tests (5d60b6d0) ``` (TK-159) More ConfigValue tests Another set of tests for config_value_spec. There will be at least one more after this one. Added a few methods to AbstractConfigObject Made the definitions of merge_origins static to match the java Implemented render/render_to_sb methods for ConfigDelayedMerge Fixed DefaultTransformer::transform method to actually compare the value type Made SimpleConfigObject::indent static SimpleConfigOrigin::merge_origins handles merging more than two tokens correctly Implemented SimpleConfigOrigin#filename to use Chris's Url class A couple methods in tokens.rb get_substitution_path_expression get_substitution_optional Commented out some failing tests in conf_parser_spec until some missing functionality is implmented ``` * Merge pull request #31 from cprice404/feature/master/TK-160-port-conf-parser-tests (f28f78ea) ``` Merge pull request #31 from cprice404/feature/master/TK-160-port-conf-parser-tests (TK-160) Initial scaffolding for conf parser tests ``` * (MAINT) add missing newline at end of file (ee375afb) * (TK-160) Change `t` to `invalid` to match upstream (012dac77) * (TK-160) Initial scaffolding for conf parser tests (04d56c9e) ``` (TK-160) Initial scaffolding for conf parser tests This commit ports over the first few ConfParser tests, and fixes a few bugs to get them passing. ``` * Merge pull request #29 from KevinCorcoran/with--vs-set (08ca846a) ``` Merge pull request #29 from KevinCorcoran/with--vs-set (maint) rename methods to match upstream ``` * Merge pull request #30 from cprice404/maint/master/add-utf8-encoding-pragma (0f3ceb58) ``` Merge pull request #30 from cprice404/maint/master/add-utf8-encoding-pragma (MAINT) Add utf-8 encoding pragma to all source files ``` * (MAINT) Add utf-8 encoding pragma to all source files (1e3b1a84) ``` (MAINT) Add utf-8 encoding pragma to all source files Because ruby-- ``` * (maint) rename methods to match upstream (7ed85816) ``` (maint) rename methods to match upstream Rename methods whose names start with "set_" or "with_" to match the names of these methods in the upstream Java project. ``` * Merge pull request #28 from KevinCorcoran/concatenation (212f6b7c) ``` Merge pull request #28 from KevinCorcoran/concatenation (TK-162) implement concatenation and substitution ``` * (maint) fix bugs identified during PR review (d608f4a5) * (TK-162) implement concatenation and substitution (bc71ba0f) ``` (TK-162) implement concatenation and substitution Initial implementation of concatenation and substitution. Ported the first test case in ConcatenationTest and as much of the production code as it took to get it to pass. ``` * Merge pull request #27 from KevinCorcoran/add-test-util (65e3ab85) ``` Merge pull request #27 from KevinCorcoran/add-test-util (maint) add TestUtils.parse_config ``` * (maint) add TestUtils.parse_config (1b47f6b4) * Merge pull request #24 from jpinsonault/tk-169-setup-travis (6e5e8698) ``` Merge pull request #24 from jpinsonault/tk-169-setup-travis (TK-169) Add travis support ``` * Updated Gemfile.lock (d8bd873e) * Removed rake dependency and Rakefile, changed .travis.yml to run rspec instead of rake (9c36b479) * Merge pull request #26 from jmccure/port-lossless-tokens (a7018fd9) ``` Merge pull request #26 from jmccure/port-lossless-tokens Add lossless comment tokens ``` * Amend lossless token test name after feedback (9b94c6a9) * Add lossless comment tokens (f14161f3) * Merge pull request #22 from jpinsonault/tk-158-port-path-tests (005a8b6f) ``` Merge pull request #22 from jpinsonault/tk-158-port-path-tests (TK-158) Port Path tests to ruby hocon ``` * Merge pull request #25 from KevinCorcoran/TK-128/fix-for-IP-addresses (1feaba32) ``` Merge pull request #25 from KevinCorcoran/TK-128/fix-for-IP-addresses (TK-128) fix tokenization of unquoted strings ``` * Merge pull request #23 from KevinCorcoran/update-gemspec (c4da445a) ``` Merge pull request #23 from KevinCorcoran/update-gemspec (maint) update gemspec with new URL and authors ``` * Changed command to use rake spec (5ae8b396) * Addressed PR feedback (bd80a3ef) ``` Addressed PR feedback Implemented Path#from_path_iterator as a constructor Fixed typos Implemented TokenIterator#to_list Fleshed out BadPath exception message handling logic ``` * (TK-128) fix tokenization of unquoted strings (224c4dfd) ``` (TK-128) fix tokenization of unquoted strings This commit fixes the tokenization of unquoted strings which might be numbers. In particular, this affects IP addresses. The tokenizer aggressively attempts to parse such strings as either a Float or Integer and relies on an error being raised to indicate when the string is not actually a valid number. However, `to_i` and `to_f` never raise execptions, so the constructors must be used instead. ``` * Changed script command (06fc855a) * Changed rspec command (37d581cc) * (TK-169) Add travis support (3fc140da) * (maint) update gemspec with new URL and authors (d6561e42) * (TK-159) Partial set of ConfigValue tests implemented (c4abaf2e) ``` (TK-159) Partial set of ConfigValue tests implemented This is a partial set of the tests for ConfigValueTest.scala We're going to get whatever functionality I've implemented in this PR so others can use it and avoid creating merge conflicts. The rest of the PRs will come as much smaller chunks of effort This branch was rebased on top of Kevin's tk-162 PR, and hopefully I merged my changes into it without breaking the tests didn't catch. Implemented parts of: ConfigDelayedMerge ConfigDelayedMergeObject ConfigReference ReplaceableMergeStack module SimpleConfigList/Object now behave like arrays/hashes by delegating required functions to their @value attribute Implemented ==() and hash() for a bunch of classes Many other small changes ``` * Merge pull request #21 from jpinsonault/tk-157-port-token-tests (45414478) ``` Merge pull request #21 from jpinsonault/tk-157-port-token-tests Tk 157 port token tests ``` * Moved shared examples into test_utils.rb and extracted out the random object examples (95a0db9b) * Extracted shared examples into separate file for reuse in other tests (f9fe3386) * (TK-158) Port Path tests to ruby hocon (bd832af6) * Removed unused TestUtils method (8d7af9ec) * (TK-157) Port Token tests to ruby hocon (921f9883) ``` (TK-157) Port Token tests to ruby hocon Ported the Token tests This involved implementing various == and hash functions for Token subclasses and Config types ``` * Merge pull request #20 from jpinsonault/tk-155-port-tokenizer-tests (5ebd16eb) ``` Merge pull request #20 from jpinsonault/tk-155-port-tokenizer-tests (TK-155) Port tokenizer tests to ruby hocon ``` * Lots of fixes for PR (039d6f42) ``` Lots of fixes for PR Used single quotes where appropriate Extracted tokenize function Changed TokenIterator.problem occurances to self.class.problem ``` * Implemented == method for token subtypes (e6a4b56b) * (TK-155) Port tokenizer tests to ruby hocon (6a2e3f83) ``` (TK-155) Port tokenizer tests to ruby hocon Ported all the java tests from the hocon library to ruby-hocon Added a few more here and there Implemented misc missing functions from the java library to get the tests passing Fixed bug in tokenizer that ignored whitespace between tokens ``` * Update gemspec for 0.0.7 release (71f475fe) * Merge pull request #18 from fpringvaldsen/json-patch (74e6ed8d) ``` Merge pull request #18 from fpringvaldsen/json-patch Allow gem to parse JSON files ``` * Merge pull request #16 from fpringvaldsen/readme-disclaimer (df334bd8) ``` Merge pull request #16 from fpringvaldsen/readme-disclaimer Add disclaimer to README ``` * Merge pull request #17 from fpringvaldsen/implement-end-token (b28b2909) ``` Merge pull request #17 from fpringvaldsen/implement-end-token Fix NameError when parsing {\n} ``` * Allow gem to parse JSON files (b72cae0b) ``` Allow gem to parse JSON files Allow the ruby hocon gem to parse JSON files. Previously, attempting to parse a JSON file would lead to an uninitialized constant error. ``` * Fix NameError when parsing {\n} (b1781fee) ``` Fix NameError when parsing {\n} Fix a NameError that would occur when a string containing {\n} was parsed. ``` * Add disclaimer to README (9f4b8df1) ``` Add disclaimer to README Add a disclaimer to the README explaining that this library is in an experimental state and some features may not work properly. ``` * Update gemspec for 0.0.6 release (1613e233) * Merge pull request #15 from waynr/maint (b25f64f7) ``` Merge pull request #15 from waynr/maint (MAINT) Fix spec tests such that they work on ruby-1.8.7-p352 ``` * Fix unecessarily strict test case. (3ed91425) ``` Fix unecessarily strict test case. As it turns out, hocon does not actually require that the output be rendered in the same order by every implementation so when running with ruby-1.9.x vs ruby-1.8.7-p352 for instance the rendered string may not have variables specified in the same order. However, hocon does require that comments be matched to their variables. This patch validates that behavior by creating a hash of config-lines mapped to lists of preceding comments and verifies that this hash is the same before and after rendering regardless of which hocon implementation created the "original" output file. Also, I find the input vs output semantics and the way variables with these names are used just a little confusing but whatever. Signed-off-by: Wayne ``` * Remove unnecessary brackets in regex. (f0a72925) ``` Remove unnecessary brackets in regex. Signed-off-by: Wayne ``` * Minor rspec testcase code cleanup. (a95cff41) ``` Minor rspec testcase code cleanup. Signed-off-by: Wayne ``` * Fix re-parsed output test cases. (e5c4d884) ``` Fix re-parsed output test cases. This testcase should not care about the rendered form of the re-parsed output since A) the hocon spec does not guarantee exact output similarity and B) testing that comments remain above the variables they describe is taken care of in the previous test case. Signed-off-by: Wayne ``` * Fix uninitialized constant error in ruby-1.8.7-p352 (53151934) ``` Fix uninitialized constant error in ruby-1.8.7-p352 Signed-off-by: Wayne ``` * Fix tokenizer for ruby-1.8.7-p352 (00fe96e2) ``` Fix tokenizer for ruby-1.8.7-p352 Signed-off-by: Wayne ``` * Fix default Rake task for ruby-1.8.7-p352 (f3bb2240) ``` Fix default Rake task for ruby-1.8.7-p352 Signed-off-by: Wayne ``` * Merge pull request #13 from cprice404/maint/master/clean-up-requires (9868fd6f) ``` Merge pull request #13 from cprice404/maint/master/clean-up-requires Clean up require statements ``` * Merge pull request #14 from fpringvaldsen/error-handling (8be042e6) ``` Merge pull request #14 from fpringvaldsen/error-handling Improve Error Handling with invalid config ``` * Improve Error Handling with invalid config (64d6166f) ``` Improve Error Handling with invalid config Implement Error Handling when an invalid config is parsed. ``` * Clean up require statements (f866d790) ### 0.0.5 - 1 Oct, 2014 (67d264f4) * Update gemspec for 0.0.5 release (67d264f4) * Merge pull request #12 from fpringvaldsen/add-methods-for-puppet (78c7afb9) ``` Merge pull request #12 from fpringvaldsen/add-methods-for-puppet Add methods required for puppet .conf module ``` * Remove commented line (d704cda2) ``` Remove commented line Delete commented constant in the Path class that was no longer needed. ``` * Move requires into Hocon module (935b2cc5) ``` Move requires into Hocon module Move requires for all files in the Hocon module into the module itself to eliminate uninitialized constant errors. ``` * Fix ConfigImpl bug and add more tests (c892c0f4) ``` Fix ConfigImpl bug and add more tests Fix bug in ConfigImpl wherein a boolean would be converted into a ConfigBoolean with value true even if the boolean is false. Increase test coverage for ConfigValueFactory tests by adding a test to ensure this bug is no longer happening, and increase test coverage of SimpleConfig spec tests by ensuring that data structures can be added to a config. ``` * Add without_path method (d23fdcde) ``` Add without_path method Port without_path method from the Java HOCON library into the SimpleConfig class. ``` * Add at_key and at_path methods (8323c2a6) ``` Add at_key and at_path methods Port the at_key and at_path methods from the Java HOCON library into the AbstractConfigValue class. ``` * Add "add" method to TokenWithComments (3430c3b7) ``` Add "add" method to TokenWithComments Port the "add" method to the TokenWithComments class. ``` * Fix requires in ConfigValueFactory (a4074d71) ``` Fix requires in ConfigValueFactory Fix the require statements so that ConfigValueFactory can be required without requiring other files. ``` * Add ConfigValueFactory (ae88610e) ``` Add ConfigValueFactory This commit adds a basic implementation of the ConfigValueFactory class. This class contains only one method, with_any_ref, which takes an object and transforms it into a ConfigObject. ``` * Add with_value method to SimpleConfig (760f7743) ``` Add with_value method to SimpleConfig Port the with_value method in the SimpleConfig class from the Java HOCON library. ``` * Add has_path method (655c1beb) ``` Add has_path method Port the has_path method in the SimpleConfig class from the Java HOCON library. ``` * Put get_value tests into their own file (9339f606) ``` Put get_value tests into their own file Move the tests of the SimpleConfig get_value method into a new file, simple_config_spec.rb ``` * Add get_value method to SimpleConfig (bfb63a35) ``` Add get_value method to SimpleConfig Add the get_value method to SimpleConfig, which allows the user to get a value from a configuration file. Add tests for this method. ``` * Merge pull request #8 from dakatsuka/add-bundler-and-rake (d204b344) ``` Merge pull request #8 from dakatsuka/add-bundler-and-rake Add bundler and rake ``` * Merge pull request #9 from dakatsuka/support-boolean (f584904f) ``` Merge pull request #9 from dakatsuka/support-boolean Support boolean ``` * Add tests for Hocon::Impl::ConfigBoolean (31fd73b6) * Implement Hocon::Impl::ConfigBoolean (927d64b7) * Add bundler and rake (c0f77be8) ### 0.0.3 - 24 Jul, 2014 (6cd552c3) * Merge pull request #6 from waynr/maint (6cd552c3) ``` Merge pull request #6 from waynr/maint Maint ``` * spec_helper: Fix EXAMPLE1 w/ empty list. (775098d9) ``` spec_helper: Fix EXAMPLE1 w/ empty list. Signed-off-by: Wayne ``` * Hocon: Fix spec tests by adding `load` and `parse` (702146cc) ``` Hocon: Fix spec tests by adding `load` and `parse` Signed-off-by: Wayne ``` * spec: Add spec tests or Hocon module. (5b65bbef) ``` spec: Add spec tests or Hocon module. The goal here is to provide an interface similar to what the JSON ruby module provides, even though this doesn't include a dump method yet. Signed-off-by: Wayne ``` * hocon.gemspec: Update gemspec for new gem release. (95d46911) ``` hocon.gemspec: Update gemspec for new gem release. Signed-off-by: Wayne ``` * parser: Don't convert anything to symbols. (0ca29caf) ``` parser: Don't convert anything to symbols. Signed-off-by: Wayne ``` * Fix Hocon::Impl::SimpleConfigList (3f77504f) ``` Fix Hocon::Impl::SimpleConfigList Chokes without this `new_copy` method. Signed-off-by: Wayne ``` * spec: Add new examples, reorganize specs. (4a9cfb1b) ``` spec: Add new examples, reorganize specs. Reorganize specs to allow for the addition of more examples. Signed-off-by: Wayne ``` * Merge pull request #5 from waynr/fix-settings-in-hocon-gemspec (14e4ed32) ``` Merge pull request #5 from waynr/fix-settings-in-hocon-gemspec gemspec: Fix settings in hocon.gemspec. ``` * Merge pull request #3 from jmccure/fix_issue_2 (38fdfc37) ``` Merge pull request #3 from jmccure/fix_issue_2 Fixed error when conf file had empty array. Issue #2 ``` * Merge pull request #4 from waynr/implement-hocon-configfactory-parsestring (464d5704) ``` Merge pull request #4 from waynr/implement-hocon-configfactory-parsestring Implement hocon configfactory parsestring ``` * Fixed error when conf file had empty array. Issue #2 (fb1248eb) ### 0.0.2 - 24 Jul, 2014 (95dffaea) * gemspec: Fix settings in hocon.gemspec. (95dffaea) ``` gemspec: Fix settings in hocon.gemspec. Signed-off-by: Wayne ``` * Implement Hocon.ConfigFactory.parse_string (430442e4) ``` Implement Hocon.ConfigFactory.parse_string Also fixes a number of typos an previously untested code paths. Signed-off-by: Wayne ``` * spec: Add Hocon::ConfigFactory.parse_string test. (1cec69f2) ``` spec: Add Hocon::ConfigFactory.parse_string test. Signed-off-by: Wayne ``` * Update README.md (9ce283b1) ### 0.0.1 - 16 Mar, 2014 (f7dbca52) * Initial release.