hocon 1.1.3 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +35 -1
- data/HISTORY.md +2140 -0
- data/README.md +125 -0
- data/bin/hocon +5 -0
- data/lib/hocon/cli.rb +225 -0
- data/lib/hocon/config_render_options.rb +3 -2
- data/lib/hocon/impl/abstract_config_value.rb +7 -10
- data/lib/hocon/impl/config_delayed_merge.rb +1 -1
- data/lib/hocon/impl/config_impl.rb +18 -0
- data/lib/hocon/impl/config_node_object.rb +1 -2
- data/lib/hocon/impl/config_node_root.rb +1 -1
- data/lib/hocon/impl/parseable.rb +6 -5
- data/lib/hocon/impl/simple_config_document.rb +1 -1
- data/lib/hocon/impl/simple_config_origin.rb +8 -2
- data/lib/hocon/version.rb +5 -0
- metadata +9 -50
- data/spec/fixtures/hocon/by_extension/cat.conf +0 -4
- data/spec/fixtures/hocon/by_extension/cat.test +0 -4
- data/spec/fixtures/hocon/by_extension/cat.test-json +0 -3
- data/spec/fixtures/hocon/with_substitution/subst.conf +0 -2
- data/spec/fixtures/parse_render/example1/input.conf +0 -21
- data/spec/fixtures/parse_render/example1/output.conf +0 -26
- data/spec/fixtures/parse_render/example1/output_nocomments.conf +0 -17
- data/spec/fixtures/parse_render/example2/input.conf +0 -10
- data/spec/fixtures/parse_render/example2/output.conf +0 -17
- data/spec/fixtures/parse_render/example2/output_nocomments.conf +0 -17
- data/spec/fixtures/parse_render/example3/input.conf +0 -2
- data/spec/fixtures/parse_render/example3/output.conf +0 -2
- data/spec/fixtures/parse_render/example4/input.json +0 -6
- data/spec/fixtures/parse_render/example4/output.conf +0 -6
- data/spec/fixtures/test_utils/resources/bom.conf +0 -2
- data/spec/fixtures/test_utils/resources/cycle.conf +0 -1
- data/spec/fixtures/test_utils/resources/file-include.conf +0 -5
- data/spec/fixtures/test_utils/resources/include-from-list.conf +0 -4
- data/spec/fixtures/test_utils/resources/subdir/bar.conf +0 -1
- data/spec/fixtures/test_utils/resources/subdir/baz.conf +0 -1
- data/spec/fixtures/test_utils/resources/subdir/foo.conf +0 -5
- data/spec/fixtures/test_utils/resources/test01.conf +0 -80
- data/spec/fixtures/test_utils/resources/test01.json +0 -4
- data/spec/fixtures/test_utils/resources/test03.conf +0 -36
- data/spec/fixtures/test_utils/resources/utf16.conf +0 -0
- data/spec/fixtures/test_utils/resources/utf8.conf +0 -2
- data/spec/fixtures/test_utils/resources//341/232/240/341/233/207/341/232/273.conf +0 -2
- data/spec/spec_helper.rb +0 -43
- data/spec/test_utils.rb +0 -757
- data/spec/unit/hocon/README.md +0 -7
- data/spec/unit/hocon/hocon_spec.rb +0 -114
- data/spec/unit/typesafe/config/README.md +0 -4
- data/spec/unit/typesafe/config/concatenation_spec.rb +0 -417
- data/spec/unit/typesafe/config/conf_parser_spec.rb +0 -832
- data/spec/unit/typesafe/config/config_document_parser_spec.rb +0 -494
- data/spec/unit/typesafe/config/config_document_spec.rb +0 -576
- data/spec/unit/typesafe/config/config_factory_spec.rb +0 -120
- data/spec/unit/typesafe/config/config_node_spec.rb +0 -552
- data/spec/unit/typesafe/config/config_value_factory_spec.rb +0 -85
- data/spec/unit/typesafe/config/config_value_spec.rb +0 -935
- data/spec/unit/typesafe/config/path_spec.rb +0 -261
- data/spec/unit/typesafe/config/public_api_spec.rb +0 -520
- data/spec/unit/typesafe/config/simple_config_spec.rb +0 -112
- data/spec/unit/typesafe/config/token_spec.rb +0 -188
- data/spec/unit/typesafe/config/tokenizer_spec.rb +0 -801
data/README.md
CHANGED
@@ -18,6 +18,12 @@ you may still run into some issues. If you find a problem, feel free to open a g
|
|
18
18
|
The implementation is intended to be as close to a line-for-line port as the two languages allow,
|
19
19
|
in hopes of making it fairly easy to port over new changesets from the Java code base over time.
|
20
20
|
|
21
|
+
Support
|
22
|
+
=======
|
23
|
+
|
24
|
+
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.
|
25
|
+
|
26
|
+
|
21
27
|
Basic Usage
|
22
28
|
===========
|
23
29
|
|
@@ -72,6 +78,125 @@ Note that a `ConfigDocument` is used primarily for simple configuration manipula
|
|
72
78
|
whitespace and comments. As such, it is not powerful as the regular `Config` API, and will not resolve
|
73
79
|
substitutions.
|
74
80
|
|
81
|
+
CLI Tool
|
82
|
+
========
|
83
|
+
The `hocon` gem comes bundles with a `hocon` command line tool which can be used to get and set values from hocon files
|
84
|
+
|
85
|
+
```
|
86
|
+
Usage: hocon [options] {get,set,unset} PATH [VALUE]
|
87
|
+
|
88
|
+
Example usages:
|
89
|
+
hocon -i settings.conf -o new_settings.conf set some.nested.value 42
|
90
|
+
hocon -f settings.conf set some.nested.value 42
|
91
|
+
cat settings.conf | hocon get some.nested.value
|
92
|
+
|
93
|
+
Subcommands:
|
94
|
+
get PATH - Returns the value at the given path
|
95
|
+
set PATH VALUE - Sets or adds the given value at the given path
|
96
|
+
unset PATH - Removes the value at the given path
|
97
|
+
|
98
|
+
Options:
|
99
|
+
-i, --in-file HOCON_FILE HOCON file to read/modify. If omitted, STDIN assumed
|
100
|
+
-o, --out-file HOCON_FILE File to be written to. If omitted, STDOUT assumed
|
101
|
+
-f, --file HOCON_FILE File to read/write to. Equivalent to setting -i/-o to the same file
|
102
|
+
-j, --json Output values from the 'get' subcommand in json format
|
103
|
+
-h, --help Show this message
|
104
|
+
-v, --version Show version
|
105
|
+
```
|
106
|
+
|
107
|
+
CLI Examples
|
108
|
+
--------
|
109
|
+
### Basic Usage
|
110
|
+
```
|
111
|
+
$ cat settings.conf
|
112
|
+
{
|
113
|
+
foo: bar
|
114
|
+
}
|
115
|
+
|
116
|
+
$ hocon -i settings.conf get foo
|
117
|
+
bar
|
118
|
+
|
119
|
+
$ hocon -i settings.conf set foo baz
|
120
|
+
|
121
|
+
$ cat settings.conf
|
122
|
+
{
|
123
|
+
foo: baz
|
124
|
+
}
|
125
|
+
|
126
|
+
# Write to a different file
|
127
|
+
$ hocon -i settings.conf -o new_settings.conf set some.nested.value 42
|
128
|
+
$ cat new_settings.conf
|
129
|
+
{
|
130
|
+
foo: bar
|
131
|
+
some: {
|
132
|
+
nested: {
|
133
|
+
value: 42
|
134
|
+
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
# Write back to the same file
|
140
|
+
$ hocon -f settings.conf set some.nested.value 42
|
141
|
+
$ cat settings.conf
|
142
|
+
{
|
143
|
+
foo: bar
|
144
|
+
some: {
|
145
|
+
nested: {
|
146
|
+
value: 42
|
147
|
+
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
```
|
152
|
+
|
153
|
+
### Complex Values
|
154
|
+
If you give `set` a properly formatted hocon dictionary or array, it will try to accept it
|
155
|
+
|
156
|
+
```
|
157
|
+
$ hocon -i settings.conf set foo "{one: [1, 2, 3], two: {hello: world}}"
|
158
|
+
{
|
159
|
+
foo: {one: [1, 2, 3], two: {hello: world}}
|
160
|
+
}
|
161
|
+
```
|
162
|
+
|
163
|
+
### Chaining
|
164
|
+
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
|
165
|
+
|
166
|
+
```
|
167
|
+
$ cat settings.conf
|
168
|
+
{
|
169
|
+
foo: bar
|
170
|
+
}
|
171
|
+
|
172
|
+
$ cat settings.conf | hocon set foo 42 | hocon set one.two three
|
173
|
+
{
|
174
|
+
foo: 42
|
175
|
+
one: {
|
176
|
+
two: three
|
177
|
+
}
|
178
|
+
}
|
179
|
+
```
|
180
|
+
|
181
|
+
### JSON Output
|
182
|
+
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
|
183
|
+
|
184
|
+
```
|
185
|
+
$ cat settings.conf
|
186
|
+
foo: {
|
187
|
+
bar: {
|
188
|
+
baz: 42
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
$ hocon -i settings.conf get foo --json
|
193
|
+
{
|
194
|
+
"bar": {
|
195
|
+
"baz": 42
|
196
|
+
}
|
197
|
+
}
|
198
|
+
```
|
199
|
+
|
75
200
|
Testing
|
76
201
|
=======
|
77
202
|
|
data/bin/hocon
ADDED
data/lib/hocon/cli.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'hocon'
|
3
|
+
require 'hocon/version'
|
4
|
+
require 'hocon/config_render_options'
|
5
|
+
require 'hocon/config_factory'
|
6
|
+
require 'hocon/config_value_factory'
|
7
|
+
require 'hocon/parser/config_document_factory'
|
8
|
+
require 'hocon/config_error'
|
9
|
+
|
10
|
+
module Hocon::CLI
|
11
|
+
# Aliases
|
12
|
+
ConfigMissingError = Hocon::ConfigError::ConfigMissingError
|
13
|
+
ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError
|
14
|
+
|
15
|
+
# List of valid subcommands
|
16
|
+
SUBCOMMANDS = ['get', 'set', 'unset']
|
17
|
+
|
18
|
+
# For when a path can't be found in a hocon config
|
19
|
+
class MissingPathError < StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
# Parses the command line flags and argument
|
23
|
+
# Returns a options hash with values for each option and argument
|
24
|
+
def self.parse_args(args)
|
25
|
+
options = {}
|
26
|
+
opt_parser = OptionParser.new do |opts|
|
27
|
+
subcommands = SUBCOMMANDS.join(',')
|
28
|
+
opts.banner = "Usage: hocon [options] {#{subcommands}} PATH [VALUE]\n\n" +
|
29
|
+
"Example usages:\n" +
|
30
|
+
" hocon -i settings.conf -o new_settings.conf set some.nested.value 42\n" +
|
31
|
+
" hocon -f settings.conf set some.nested.value 42\n" +
|
32
|
+
" cat settings.conf | hocon get some.nested.value\n\n" +
|
33
|
+
"Subcommands:\n" +
|
34
|
+
" get PATH - Returns the value at the given path\n" +
|
35
|
+
" set PATH VALUE - Sets or adds the given value at the given path\n" +
|
36
|
+
" unset PATH - Removes the value at the given path"
|
37
|
+
|
38
|
+
opts.separator('')
|
39
|
+
opts.separator('Options:')
|
40
|
+
|
41
|
+
in_file_description = 'HOCON file to read/modify. If omitted, STDIN assumed'
|
42
|
+
opts.on('-i', '--in-file HOCON_FILE', in_file_description) do |in_file|
|
43
|
+
options[:in_file] = in_file
|
44
|
+
end
|
45
|
+
|
46
|
+
out_file_description = 'File to be written to. If omitted, STDOUT assumed'
|
47
|
+
opts.on('-o', '--out-file HOCON_FILE', out_file_description) do |out_file|
|
48
|
+
options[:out_file] = out_file
|
49
|
+
end
|
50
|
+
|
51
|
+
file_description = 'File to read/write to. Equivalent to setting -i/-o to the same file'
|
52
|
+
opts.on('-f', '--file HOCON_FILE', file_description) do |file|
|
53
|
+
options[:file] = file
|
54
|
+
end
|
55
|
+
|
56
|
+
json_description = "Output values from the 'get' subcommand in json format"
|
57
|
+
opts.on('-j', '--json', json_description) do |json|
|
58
|
+
options[:json] = json
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
62
|
+
puts opts
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
67
|
+
puts Hocon::Version::STRING
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# parse! returns the argument list minus all the flags it found
|
72
|
+
remaining_args = opt_parser.parse!(args)
|
73
|
+
|
74
|
+
# Ensure -i and -o aren't used at the same time as -f
|
75
|
+
if (options[:in_file] || options[:out_file]) && options[:file]
|
76
|
+
exit_with_usage_and_error(opt_parser, "--file can't be used with --in-file or --out-file")
|
77
|
+
end
|
78
|
+
|
79
|
+
# If --file is used, set --in/out-file to the same file
|
80
|
+
if options[:file]
|
81
|
+
options[:in_file] = options[:file]
|
82
|
+
options[:out_file] = options[:file]
|
83
|
+
end
|
84
|
+
|
85
|
+
no_subcommand_error(opt_parser) unless remaining_args.size > 0
|
86
|
+
|
87
|
+
# Assume the first arg is the subcommand
|
88
|
+
subcommand = remaining_args.shift
|
89
|
+
options[:subcommand] = subcommand
|
90
|
+
|
91
|
+
case subcommand
|
92
|
+
when 'set'
|
93
|
+
subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 2
|
94
|
+
options[:path] = remaining_args.shift
|
95
|
+
options[:new_value] = remaining_args.shift
|
96
|
+
|
97
|
+
when 'get', 'unset'
|
98
|
+
subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 1
|
99
|
+
options[:path] = remaining_args.shift
|
100
|
+
|
101
|
+
else
|
102
|
+
invalid_subcommand_error(subcommand, opt_parser)
|
103
|
+
end
|
104
|
+
|
105
|
+
options
|
106
|
+
end
|
107
|
+
|
108
|
+
# Main entry point into the script
|
109
|
+
# Calls the appropriate subcommand and handles errors raised from the subcommands
|
110
|
+
def self.main(opts)
|
111
|
+
hocon_text = get_hocon_file(opts[:in_file])
|
112
|
+
|
113
|
+
begin
|
114
|
+
case opts[:subcommand]
|
115
|
+
when 'get'
|
116
|
+
puts do_get(opts, hocon_text)
|
117
|
+
when 'set'
|
118
|
+
print_or_write(do_set(opts, hocon_text), opts[:out_file])
|
119
|
+
when 'unset'
|
120
|
+
print_or_write(do_unset(opts, hocon_text), opts[:out_file])
|
121
|
+
end
|
122
|
+
|
123
|
+
rescue MissingPathError
|
124
|
+
exit_with_error("Can't find the given path: '#{opts[:path]}'")
|
125
|
+
end
|
126
|
+
|
127
|
+
exit
|
128
|
+
end
|
129
|
+
|
130
|
+
# Entry point for the 'get' subcommand
|
131
|
+
# Returns a string representation of the the value at the path given on the
|
132
|
+
# command line
|
133
|
+
def self.do_get(opts, hocon_text)
|
134
|
+
config = Hocon::ConfigFactory.parse_string(hocon_text)
|
135
|
+
unless config.has_path?(opts[:path])
|
136
|
+
raise MissingPathError.new
|
137
|
+
end
|
138
|
+
|
139
|
+
value = config.get_any_ref(opts[:path])
|
140
|
+
|
141
|
+
render_options = Hocon::ConfigRenderOptions.defaults
|
142
|
+
# Otherwise weird comments show up in the output
|
143
|
+
render_options.origin_comments = false
|
144
|
+
# If json is false, the hocon format is used
|
145
|
+
render_options.json = opts[:json]
|
146
|
+
# Output colons between keys and values
|
147
|
+
render_options.key_value_separator = :colon
|
148
|
+
|
149
|
+
Hocon::ConfigValueFactory.from_any_ref(value).render(render_options)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Entry point for the 'set' subcommand
|
153
|
+
# Returns a string representation of the HOCON config after adding/replacing
|
154
|
+
# the value at the given path with the given value
|
155
|
+
def self.do_set(opts, hocon_text)
|
156
|
+
config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text)
|
157
|
+
modified_config_doc = config_doc.set_value(opts[:path], opts[:new_value])
|
158
|
+
|
159
|
+
modified_config_doc.render
|
160
|
+
end
|
161
|
+
|
162
|
+
# Entry point for the 'unset' subcommand
|
163
|
+
# Returns a string representation of the HOCON config after removing the
|
164
|
+
# value at the given path
|
165
|
+
def self.do_unset(opts, hocon_text)
|
166
|
+
config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text)
|
167
|
+
unless config_doc.has_value?(opts[:path])
|
168
|
+
raise MissingPathError.new
|
169
|
+
end
|
170
|
+
|
171
|
+
modified_config_doc = config_doc.remove_value(opts[:path])
|
172
|
+
|
173
|
+
modified_config_doc.render
|
174
|
+
end
|
175
|
+
|
176
|
+
# If a file is provided, return it's contents. Otherwise read from STDIN
|
177
|
+
def self.get_hocon_file(in_file)
|
178
|
+
if in_file
|
179
|
+
File.read(in_file)
|
180
|
+
else
|
181
|
+
STDIN.read
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Print an error message and exit the program
|
186
|
+
def self.exit_with_error(message)
|
187
|
+
STDERR.puts "Error: #{message}"
|
188
|
+
exit(1)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Print an error message and usage, then exit the program
|
192
|
+
def self.exit_with_usage_and_error(opt_parser, message)
|
193
|
+
STDERR.puts opt_parser
|
194
|
+
exit_with_error(message)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Exits with an error saying there aren't enough arguments found for a given
|
198
|
+
# subcommand. Prints the usage
|
199
|
+
def self.subcommand_arguments_error(subcommand, opt_parser)
|
200
|
+
error_message = "Too few arguments for '#{subcommand}' subcommand"
|
201
|
+
exit_with_usage_and_error(opt_parser, error_message)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Exits with an error for when no subcommand is supplied on the command line.
|
205
|
+
# Prints the usage
|
206
|
+
def self.no_subcommand_error(opt_parser)
|
207
|
+
error_message = "Must specify subcommand from [#{SUBCOMMANDS.join(', ')}]"
|
208
|
+
exit_with_usage_and_error(opt_parser, error_message)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Exits with an error for when a subcommand doesn't exist. Prints the usage
|
212
|
+
def self.invalid_subcommand_error(subcommand, opt_parser)
|
213
|
+
error_message = "Invalid subcommand '#{subcommand}', must be one of [#{SUBCOMMANDS.join(', ')}]"
|
214
|
+
exit_with_usage_and_error(opt_parser, error_message)
|
215
|
+
end
|
216
|
+
|
217
|
+
# If out_file is not nil, write to that file. Otherwise print to STDOUT
|
218
|
+
def self.print_or_write(string, out_file)
|
219
|
+
if out_file
|
220
|
+
File.open(out_file, 'w') { |file| file.write(string) }
|
221
|
+
else
|
222
|
+
puts string
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -3,14 +3,15 @@
|
|
3
3
|
require 'hocon'
|
4
4
|
|
5
5
|
class Hocon::ConfigRenderOptions
|
6
|
-
def initialize(origin_comments, comments, formatted, json)
|
6
|
+
def initialize(origin_comments, comments, formatted, json, key_value_separator=:equals)
|
7
7
|
@origin_comments = origin_comments
|
8
8
|
@comments = comments
|
9
9
|
@formatted = formatted
|
10
10
|
@json = json
|
11
|
+
@key_value_separator = key_value_separator
|
11
12
|
end
|
12
13
|
|
13
|
-
attr_accessor :origin_comments, :comments, :formatted, :json
|
14
|
+
attr_accessor :origin_comments, :comments, :formatted, :json, :key_value_separator
|
14
15
|
|
15
16
|
def origin_comments?
|
16
17
|
@origin_comments
|
@@ -314,20 +314,17 @@ module Hocon::Impl::AbstractConfigValue
|
|
314
314
|
|
315
315
|
if options.json?
|
316
316
|
if options.formatted?
|
317
|
-
sb << "
|
317
|
+
sb << ": "
|
318
318
|
else
|
319
319
|
sb << ":"
|
320
320
|
end
|
321
321
|
else
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
sb << "="
|
329
|
-
end
|
330
|
-
end
|
322
|
+
case options.key_value_separator
|
323
|
+
when :colon
|
324
|
+
sb << ": "
|
325
|
+
else
|
326
|
+
sb << "="
|
327
|
+
end end
|
331
328
|
end
|
332
329
|
render_value_to_sb(sb, indent, at_root, options)
|
333
330
|
end
|
@@ -237,9 +237,27 @@ class Hocon::Impl::ConfigImpl
|
|
237
237
|
def self.load_env_variables
|
238
238
|
env = ENV
|
239
239
|
m = {}
|
240
|
+
lists = {}
|
240
241
|
env.each do |key, value|
|
241
242
|
m[key] = Hocon::Impl::ConfigString::Quoted.new(
|
242
243
|
Hocon::Impl::SimpleConfigOrigin.new_simple("env var #{key}"), value)
|
244
|
+
# find env var arrays specified like FOO.0, FOO.1
|
245
|
+
if key =~ /^(.*)\.(\d+)$/
|
246
|
+
name, index = $1, Integer($2)
|
247
|
+
# values are added unordered
|
248
|
+
(lists[name] ||= {})[index] = value
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
lists.each do |key, values|
|
253
|
+
origin = Hocon::Impl::SimpleConfigOrigin.new_simple("env var list #{key}")
|
254
|
+
m[key] = Hocon::Impl::SimpleConfigList.new(
|
255
|
+
origin,
|
256
|
+
# out of order env vars FOO.1, FOO.0 are sorted here
|
257
|
+
values.sort.map do |v|
|
258
|
+
Hocon::Impl::ConfigString::Quoted.new(origin, v[1])
|
259
|
+
end
|
260
|
+
)
|
243
261
|
end
|
244
262
|
|
245
263
|
Hocon::Impl::SimpleConfigObject.new(
|