levels 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +12 -0
- data/Guardfile +14 -0
- data/LICENSE +22 -0
- data/README.md +315 -0
- data/Rakefile +28 -0
- data/bin/levels +130 -0
- data/examples/01_base.rb +6 -0
- data/examples/01_merge_to_json.sh +27 -0
- data/examples/01_prod.json +8 -0
- data/examples/02_base.rb +4 -0
- data/examples/02_merge_with_file.sh +20 -0
- data/examples/02_value +1 -0
- data/levels.gemspec +20 -0
- data/lib/levels.rb +77 -0
- data/lib/levels/audit.rb +24 -0
- data/lib/levels/audit/group_observer.rb +26 -0
- data/lib/levels/audit/nested_group_observer.rb +37 -0
- data/lib/levels/audit/root_observer.rb +63 -0
- data/lib/levels/audit/value.rb +64 -0
- data/lib/levels/audit/value_observer.rb +46 -0
- data/lib/levels/audit/values.rb +66 -0
- data/lib/levels/configuration.rb +98 -0
- data/lib/levels/configured_group.rb +62 -0
- data/lib/levels/event_handler.rb +127 -0
- data/lib/levels/group.rb +61 -0
- data/lib/levels/input/json.rb +17 -0
- data/lib/levels/input/ruby.rb +120 -0
- data/lib/levels/input/system.rb +63 -0
- data/lib/levels/input/yaml.rb +17 -0
- data/lib/levels/key.rb +28 -0
- data/lib/levels/key_values.rb +54 -0
- data/lib/levels/lazy_evaluator.rb +54 -0
- data/lib/levels/level.rb +80 -0
- data/lib/levels/method_missing.rb +14 -0
- data/lib/levels/output/json.rb +33 -0
- data/lib/levels/output/system.rb +29 -0
- data/lib/levels/output/yaml.rb +19 -0
- data/lib/levels/runtime.rb +30 -0
- data/lib/levels/setup.rb +132 -0
- data/lib/levels/system/constants.rb +8 -0
- data/lib/levels/system/key_formatter.rb +15 -0
- data/lib/levels/system/key_generator.rb +50 -0
- data/lib/levels/system/key_parser.rb +67 -0
- data/lib/levels/version.rb +3 -0
- data/test/acceptance/audit_test.rb +105 -0
- data/test/acceptance/event_handler_test.rb +43 -0
- data/test/acceptance/read_json_test.rb +35 -0
- data/test/acceptance/read_ruby_test.rb +117 -0
- data/test/acceptance/read_system_test.rb +121 -0
- data/test/acceptance/read_yaml_test.rb +38 -0
- data/test/acceptance/setup_test.rb +115 -0
- data/test/acceptance/write_json_test.rb +39 -0
- data/test/acceptance/write_system_test.rb +68 -0
- data/test/acceptance/write_yaml_test.rb +33 -0
- data/test/bin/merge_test.rb +194 -0
- data/test/bin/options_test.rb +41 -0
- data/test/helper.rb +12 -0
- data/test/support/acceptance_spec.rb +58 -0
- data/test/support/base_spec.rb +14 -0
- data/test/support/bin_spec.rb +65 -0
- data/test/support/tempfile_helper.rb +35 -0
- data/test/unit/audit/group_observer_test.rb +24 -0
- data/test/unit/audit/nested_group_observer_test.rb +28 -0
- data/test/unit/audit/root_observer_test.rb +54 -0
- data/test/unit/audit/value_observer_test.rb +63 -0
- data/test/unit/audit/value_test.rb +41 -0
- data/test/unit/audit/values_test.rb +86 -0
- data/test/unit/configuration_test.rb +72 -0
- data/test/unit/configured_group_test.rb +75 -0
- data/test/unit/group_test.rb +105 -0
- data/test/unit/input/json_test.rb +32 -0
- data/test/unit/input/ruby_test.rb +140 -0
- data/test/unit/input/system_test.rb +59 -0
- data/test/unit/input/yaml_test.rb +33 -0
- data/test/unit/key_test.rb +45 -0
- data/test/unit/key_values_test.rb +106 -0
- data/test/unit/lazy_evaluator_test.rb +38 -0
- data/test/unit/level_test.rb +89 -0
- data/test/unit/levels_test.rb +23 -0
- data/test/unit/output/json_test.rb +55 -0
- data/test/unit/output/system_test.rb +32 -0
- data/test/unit/output/yaml_test.rb +38 -0
- data/test/unit/runtime_test.rb +40 -0
- data/test/unit/system/key_formatter_test.rb +43 -0
- data/test/unit/system/key_generator_test.rb +21 -0
- data/test/unit/system/key_parser_test.rb +207 -0
- metadata +215 -0
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p194
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'minitest' do
|
5
|
+
watch(%r|^test/(.*)\/?(.*)_test\.rb|)
|
6
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m|
|
7
|
+
[
|
8
|
+
"test/#{m[1]}#{m[2]}_test.rb",
|
9
|
+
"test/#{m[1].sub(/^levels/, 'unit')}#{m[2]}_test.rb"
|
10
|
+
]
|
11
|
+
}
|
12
|
+
watch(%r|^test/helper\.rb|) { "test" }
|
13
|
+
watch(%r|^bin/|) { "test/bin" }
|
14
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ryan Carver
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
# Levels
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/rcarver/levels.png)](http://travis-ci.org/rcarver/levels)
|
4
|
+
|
5
|
+
Levels is a tool for merging configuration data. A level is a set of
|
6
|
+
key/value pairs that represent your data. Multiple levels, written in a
|
7
|
+
variety of formats can be merged in a predictable, useful way to form
|
8
|
+
a final configuration.
|
9
|
+
|
10
|
+
> **KRAMER:** *I'm completely changing the configuration of the apartment. You're not gonna believe it when you see it. A whole new lifestyle.*
|
11
|
+
|
12
|
+
> **JERRY:** *What are you doing?*
|
13
|
+
|
14
|
+
> **KRAMER:** *Levels.*
|
15
|
+
|
16
|
+
## Creating a level
|
17
|
+
|
18
|
+
A level is made up of one or more groups. A group is a set of key/value
|
19
|
+
pairs. To describe a very simple web application made up of a server and
|
20
|
+
a task queue, you could write this (in JSON).
|
21
|
+
|
22
|
+
```json
|
23
|
+
{
|
24
|
+
"server": {
|
25
|
+
"hostname": "example.com"
|
26
|
+
},
|
27
|
+
"task_queue": {
|
28
|
+
"workers": 5,
|
29
|
+
"queues": ["high", "low"]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
Now consider having a common "base" configuration, with slight
|
35
|
+
differences in development and production. Our base configuration
|
36
|
+
defines the possible keys, with default values.
|
37
|
+
|
38
|
+
A "production" level can override the relevant values like this.
|
39
|
+
|
40
|
+
```json
|
41
|
+
{
|
42
|
+
"server": {
|
43
|
+
"hostname": "example.com"
|
44
|
+
},
|
45
|
+
"task_queue": {
|
46
|
+
"workers": 5
|
47
|
+
}
|
48
|
+
}
|
49
|
+
```
|
50
|
+
|
51
|
+
The system's environment may be used as a level. To alter any value at
|
52
|
+
runtime, follow a convention to set the appropriate environment
|
53
|
+
variable.
|
54
|
+
|
55
|
+
```bash
|
56
|
+
TASK_QUEUE_WORKERS="10"
|
57
|
+
```
|
58
|
+
|
59
|
+
### Writing a level
|
60
|
+
|
61
|
+
A level may be written in one of many formats.
|
62
|
+
|
63
|
+
* **RUBY** is the most common and powerful for hand written configs.
|
64
|
+
* **JSON** is convenient for machine generated configs.
|
65
|
+
* **YAML** is good for both hand written and machine generated configs.
|
66
|
+
* **Environment Variables** are useful for local or runtime
|
67
|
+
configuration. This syntax may not be used for the "base" level.
|
68
|
+
|
69
|
+
#### Data Types
|
70
|
+
|
71
|
+
Levels has a limited understanding of data types by design. The guiding
|
72
|
+
principles are:
|
73
|
+
|
74
|
+
* It must be possible to represent any value in an environment
|
75
|
+
variable.
|
76
|
+
* Use only types that are native in JSON.
|
77
|
+
|
78
|
+
Therefore, Levels only supports the following types:
|
79
|
+
|
80
|
+
* **string** (Ruby `String`)
|
81
|
+
* **integer** (Ruby `Fixnum`)
|
82
|
+
* **float** (Ruby `Float`)
|
83
|
+
* **boolean** (Ruby `TrueClass` or `FalseClass`)
|
84
|
+
* **array** (Ruby `Array`) of values, which are also typed.
|
85
|
+
* **null** (Ruby `NilClass`)
|
86
|
+
|
87
|
+
Notice that JSON's Object is not supported. This is because groups are
|
88
|
+
objects, so key/values pairs are already available. It's difficult to
|
89
|
+
represent key/value pairs in an environment variable, so it fails that
|
90
|
+
test as well.
|
91
|
+
|
92
|
+
Fortunately, these simple types are perfectly adequate for the purposes
|
93
|
+
of system configuration.
|
94
|
+
|
95
|
+
#### Ruby Syntax
|
96
|
+
|
97
|
+
The Ruby DSL is a clean, simple format. It aims to be readable, writable and
|
98
|
+
editable. It looks like this:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
group :server
|
102
|
+
set hostname: "example.com"
|
103
|
+
|
104
|
+
group :task_queue
|
105
|
+
set workers: 5
|
106
|
+
set queues: ["high", "low"]
|
107
|
+
```
|
108
|
+
|
109
|
+
The Ruby syntax supports **computed values**.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
group :task_queue
|
113
|
+
set queues: -> { [server.hostname, "high", "low"] }
|
114
|
+
```
|
115
|
+
|
116
|
+
##### Extending the Ruby Runtime
|
117
|
+
|
118
|
+
To extend the runtime environment, add methods to `Levels::Runtime`.
|
119
|
+
Those methods can return a value directly, or return a Proc for
|
120
|
+
lazy evaluation.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
module Levels::Runtime
|
124
|
+
# This helper decrypts a value using the merged value of
|
125
|
+
# `secret_keys.sha_key`.
|
126
|
+
def encrypted(encrypted_value)
|
127
|
+
-> { SHA.decrypt(encrypted_value, secret_keys.sha_key) }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
With this runtime helper, you can now write:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
group :aws
|
136
|
+
set secret_key: encrypted("your aws secret key")
|
137
|
+
```
|
138
|
+
|
139
|
+
##### Builtin runtime extensions
|
140
|
+
|
141
|
+
These functions are provided by the default Levels Runtime.
|
142
|
+
|
143
|
+
* `file(path)` reads the value from a file. The file path is
|
144
|
+
interpreted as relative to the Ruby file unless it begins with '/'.
|
145
|
+
File storage can be useful when configuring large strings such as
|
146
|
+
SSL keys.
|
147
|
+
|
148
|
+
#### JSON Syntax
|
149
|
+
|
150
|
+
JSON syntax is straightforward. Because the datatypes supported by
|
151
|
+
Levels are the same as supported by JSON, there's nothing else you need
|
152
|
+
to know.
|
153
|
+
|
154
|
+
```json
|
155
|
+
{
|
156
|
+
"server": {
|
157
|
+
"hostname": "example.com"
|
158
|
+
},
|
159
|
+
"task_queue": {
|
160
|
+
"workers": 5,
|
161
|
+
"queues": ["high", "low"]
|
162
|
+
}
|
163
|
+
}
|
164
|
+
```
|
165
|
+
|
166
|
+
#### YAML Syntax
|
167
|
+
|
168
|
+
YAML syntax is also exactly as you would expect.
|
169
|
+
|
170
|
+
```yaml
|
171
|
+
---
|
172
|
+
server:
|
173
|
+
hostname: example.com
|
174
|
+
task_queue:
|
175
|
+
workers: 5
|
176
|
+
queues:
|
177
|
+
- high
|
178
|
+
- low
|
179
|
+
```
|
180
|
+
|
181
|
+
#### Environment Variables syntax
|
182
|
+
|
183
|
+
The environment variables syntax has rules for defining keys and values.
|
184
|
+
|
185
|
+
The format of each key is `[PREFIX]<GROUP>_<KEY>`.
|
186
|
+
|
187
|
+
* `PREFIX` is an optional prefix for all keys.
|
188
|
+
* `GROUP` is the name of the group in all caps.
|
189
|
+
* `KEY` is the name of the key in all caps.
|
190
|
+
* `GROUP` and `KEY` are separated by an underscore (`_`).
|
191
|
+
|
192
|
+
The example looks like this (without a prefix).
|
193
|
+
|
194
|
+
```sh
|
195
|
+
SERVER_HOSTNAME="example.com"
|
196
|
+
TASK_QUEUE_WORKERS="5"
|
197
|
+
TASK_QUEUE_QUEUES="high:low"
|
198
|
+
```
|
199
|
+
|
200
|
+
##### Typecasting
|
201
|
+
|
202
|
+
You'll notice that `TASK_QUEUE_WORKERS` should be an integer, and
|
203
|
+
`TASK_QUEUE_QUEUES` should be an array. Levels will typecast each value
|
204
|
+
based on the key's type in the "base" level. Or, you may define each
|
205
|
+
value's type explicitly.
|
206
|
+
|
207
|
+
To set the type of a value, set `<GROUP>_<KEY>_TYPE` to one of the
|
208
|
+
following:
|
209
|
+
|
210
|
+
* `string` - The value is taken as is.
|
211
|
+
* `integer` - The value is converted to an integer via Ruby's `to_i`.
|
212
|
+
* `float` - The value is converted to a float via Ruby's `to_f`.
|
213
|
+
* `boolean` - The value is `true` if it's "true" or "1", else `false`.
|
214
|
+
* `array` - The value is split using colon (`:`) or
|
215
|
+
`<GROUP>_<KEY>_DELIMITER`. The values of the resulting array may be
|
216
|
+
typecast using `<GROUP>_<KEY>_TYPE_TYPE`.
|
217
|
+
|
218
|
+
Any value may be set to Ruby's `nil` (`NULL`) by setting it to an empty
|
219
|
+
string.
|
220
|
+
|
221
|
+
Some examples:
|
222
|
+
|
223
|
+
```sh
|
224
|
+
SAMPLE_MY_NULL=""
|
225
|
+
|
226
|
+
SAMPLE_MY_INT="123"
|
227
|
+
SAMPLE_MY_INT_TYPE="integer"
|
228
|
+
|
229
|
+
SAMPLE_MY_BOOL="true"
|
230
|
+
SAMPLE_MY_BOOL_TYPE="boolean"
|
231
|
+
|
232
|
+
SAMPLE_MY_STRING_ARRAY="a:b:c"
|
233
|
+
SAMPLE_MY_STRING_ARRAY_TYPE="array"
|
234
|
+
|
235
|
+
SAMPLE_MY_INT_ARRAY="1:2:3"
|
236
|
+
SAMPLE_MY_INT_ARRAY_TYPE="array"
|
237
|
+
SAMPLE_MY_INT_ARRAY_TYPE_TYPE="integer"
|
238
|
+
|
239
|
+
SAMPLE_MY_CSV_ARRAY="one,two,three"
|
240
|
+
SAMPLE_MY_CSV_ARRAY_TYPE="array"
|
241
|
+
SAMPLE_MY_CSV_ARRAY_DELIMITER=","
|
242
|
+
```
|
243
|
+
|
244
|
+
## Using a Configuration
|
245
|
+
|
246
|
+
Once a level has been written, you can read and merge it. Once merged
|
247
|
+
into a Configuration, you can use it at runtime in a Ruby process, or
|
248
|
+
output it as JSON, YAML or environment variables.
|
249
|
+
|
250
|
+
Any number of levels, including the system environment, may be merged.
|
251
|
+
The system environment is typically merged last, but it's not required.
|
252
|
+
|
253
|
+
**From the command line**, Levels can generate JSON, YAML or environment
|
254
|
+
variables. The generated configuration is written to STDOUT. Both JSON
|
255
|
+
and Environment Variables look exactly like the input formats above.
|
256
|
+
|
257
|
+
```sh
|
258
|
+
levels \
|
259
|
+
--output json \
|
260
|
+
--level "Base" \
|
261
|
+
--level "Prod" \
|
262
|
+
--system \
|
263
|
+
base.rb \
|
264
|
+
prod.json
|
265
|
+
```
|
266
|
+
|
267
|
+
**Within a Ruby program**, a `Levels::Configuration` is an object. You
|
268
|
+
can build one with `Levels.merge`.
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# Merge multiple input levels from various sources - file, API and
|
272
|
+
# environment variables.
|
273
|
+
config = Levels.merge do |levels|
|
274
|
+
levels.add "Base", HTTP.get("https://server/config.json")
|
275
|
+
levels.add "Prod", "prod.json"
|
276
|
+
levels.add_system
|
277
|
+
end
|
278
|
+
```
|
279
|
+
|
280
|
+
The resulting `config` object works like this.
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
# Dot syntax.
|
284
|
+
config.server.hostname # => "example.com"
|
285
|
+
config.task_queue.workers # => 5
|
286
|
+
config.task_queue.queues # => ["high", "low"]
|
287
|
+
|
288
|
+
# Hash syntax.
|
289
|
+
config[:server][:hostname] # => "example.com"
|
290
|
+
config[:task_queue][:workers] # => 5
|
291
|
+
config[:task_queue][:queues] # => ["high", "low"]
|
292
|
+
```
|
293
|
+
|
294
|
+
An attempt to read an unknown group or key will throw an exception.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
config.some_group # raises Levels::UnknownGroup
|
298
|
+
config.server.some_value # raises Levels::UnknownKey
|
299
|
+
```
|
300
|
+
|
301
|
+
You can find out if a group or key exists.
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
config.defined?(:other) # => false
|
305
|
+
config.defined?(:server) # => true
|
306
|
+
config.server.defined?(:other) # => false
|
307
|
+
config.server.defined?(:hostname) # => true
|
308
|
+
```
|
309
|
+
|
310
|
+
## Author
|
311
|
+
|
312
|
+
Ryan Carver / @rcarver
|
313
|
+
|
314
|
+
Copyright (c) Ryan Carver 2012. Made available under the MIT license.
|
315
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
desc "Run all tests"
|
4
|
+
task :default => :testall
|
5
|
+
|
6
|
+
desc "Run all of the tests"
|
7
|
+
task :testall => [:test, :examples]
|
8
|
+
|
9
|
+
desc "Run the unit tests"
|
10
|
+
Rake::TestTask.new do |t|
|
11
|
+
t.libs.push "lib", "test"
|
12
|
+
t.test_files = FileList['test/**/*_test.rb']
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run the examples"
|
17
|
+
task :examples do
|
18
|
+
Dir["examples/*.sh"].each do |script|
|
19
|
+
sh script
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'bundler'
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
rescue
|
27
|
+
STDERR.puts "bundler is not available"
|
28
|
+
end
|
data/bin/levels
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'levels'
|
4
|
+
require 'optparse'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
# If "--system" is the last flag, and no PREFIX is set, then
|
8
|
+
# the first file argument will be interpreted as the PREFIX.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# # This would be interpreted as "one.rb is the system prefix".
|
13
|
+
# --level One --system one.rb
|
14
|
+
# # What we want is
|
15
|
+
# --level One --system '' one.rb
|
16
|
+
#
|
17
|
+
def fix_system_is_last_arg(argv)
|
18
|
+
index = ARGV.index("--system") or return
|
19
|
+
value = argv[index + 1] or return
|
20
|
+
|
21
|
+
unless value =~ /^-/ || value =~ /^[A-Z_]/
|
22
|
+
ARGV.insert(index + 1, "")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
fix_system_is_last_arg(ARGV)
|
27
|
+
|
28
|
+
# By default we'll output JSON
|
29
|
+
@output = true
|
30
|
+
@output_format = "json"
|
31
|
+
|
32
|
+
# Colorize by default.
|
33
|
+
@colorize = true
|
34
|
+
|
35
|
+
# If output is "system", change the prefix.
|
36
|
+
@system_output_prefix = nil
|
37
|
+
|
38
|
+
# Read input from the system.
|
39
|
+
@system = false
|
40
|
+
@system_input_prefix = nil
|
41
|
+
|
42
|
+
# Accumulate the names of levels for file arguments.
|
43
|
+
@level_names = []
|
44
|
+
|
45
|
+
OptionParser.new do |opts|
|
46
|
+
|
47
|
+
opts.banner = "Usage: levels [options] [files]"
|
48
|
+
|
49
|
+
opts.on("-l", "--level [NAME]", "Name the level from a file.") do |n|
|
50
|
+
@level_names << n
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-s", "--system [PREFIX]", "Read the system as a level.") do |p|
|
54
|
+
@system = true
|
55
|
+
@system_input_prefix = p if p && !p.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on("-o", "--output FORMAT", "The format to output. (json, system)") do |o|
|
59
|
+
@output_format = o
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-p", "--prefix PREFIX", "Prefix for system output.") do |p|
|
63
|
+
@system_output_prefix = p
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on("-n", "--no-output", "Don't output the result.") do
|
67
|
+
@output = false
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on("--[no-]color", "Colorize the output.") do |bool|
|
71
|
+
@colorize = bool
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on("-v", "--version", "Show the levels version.") do
|
75
|
+
STDOUT.puts "Levels #{Levels::VERSION}"
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on("-h", "--help", "Show this help.") do
|
79
|
+
STDOUT.puts opts.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
end.parse!
|
83
|
+
|
84
|
+
# Files are any remaining arguments after parsing.
|
85
|
+
@files = ARGV.dup
|
86
|
+
|
87
|
+
@setup = Levels.setup
|
88
|
+
|
89
|
+
# Read each file into a level.
|
90
|
+
@files.each.with_index do |file, index|
|
91
|
+
pn = Pathname.new(file)
|
92
|
+
level_name = @level_names[index] || pn.basename.to_s
|
93
|
+
STDERR.puts "Add level #{level_name.inspect} from #{pn.basename}"
|
94
|
+
|
95
|
+
@setup.add level_name, file
|
96
|
+
end
|
97
|
+
|
98
|
+
# Read the system using the existing levels as a base, then add it
|
99
|
+
# as another level.
|
100
|
+
if @system
|
101
|
+
level_name = "System Environment"
|
102
|
+
if @system_input_prefix
|
103
|
+
STDERR.puts "Add level #{level_name.inspect} with prefix #{@system_input_prefix}"
|
104
|
+
else
|
105
|
+
STDERR.puts "Add level #{level_name.inspect}"
|
106
|
+
end
|
107
|
+
@setup.add_system @system_input_prefix, level_name, ENV
|
108
|
+
end
|
109
|
+
|
110
|
+
# Write the configuration to stdout.
|
111
|
+
if @output && (@files.any? || @system)
|
112
|
+
configuration = @setup.merge
|
113
|
+
configuration.event_handler = Levels::CliEventHandler.new(STDERR, @colorize)
|
114
|
+
|
115
|
+
output = nil
|
116
|
+
|
117
|
+
case @output_format
|
118
|
+
when "json"
|
119
|
+
output = Levels::Output::JSON.new
|
120
|
+
when "yaml"
|
121
|
+
output = Levels::Output::YAML.new
|
122
|
+
when "system"
|
123
|
+
key_formatter = Levels::System::KeyFormatter.new(@system_output_prefix)
|
124
|
+
output = Levels::Output::System.new(key_formatter)
|
125
|
+
end
|
126
|
+
|
127
|
+
if output
|
128
|
+
STDOUT.puts output.generate(configuration.to_enum)
|
129
|
+
end
|
130
|
+
end
|