stacked_config 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +287 -25
- data/lib/stacked_config/orchestrator.rb +0 -24
- data/lib/stacked_config/program_description_helper.rb +26 -0
- data/lib/stacked_config/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbaae6cc93a17d8d15943af382d2683addc479c2
|
4
|
+
data.tar.gz: 3949626e5a158e99d7cdcf1bcdf6a32852966bb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a9e072826b8bccf78c16d30479ebfda5f92408c3fa4bf92661bcf11a48efc7c1c0ce7f715dcafad384917ef8f54f6787160af5d6369336e6f3a5a2760e1e926
|
7
|
+
data.tar.gz: bcb3241c7a790dce29df30c1a219cb3589d9a0ec9f4e859842bc5e4ea63c515e27c47d08582fdc1d1d9fd1763c1ba70f2376ddab0677f321570915b036dfa7e6
|
data/README.md
CHANGED
@@ -2,18 +2,22 @@
|
|
2
2
|
[![Build Status](https://travis-ci.org/lbriais/stacked_config.svg)](https://travis-ci.org/lbriais/stacked_config)
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/stacked_config.svg)](http://badge.fury.io/rb/stacked_config)
|
4
4
|
|
5
|
+
If you need to manage config files accross the system, some of them belonging to the administrator some to the user
|
6
|
+
running the application, some coming from the command line and more, __[This Gem]
|
7
|
+
(http://rubygems.org/gems/stacked_config) is made for you__ !
|
8
|
+
|
5
9
|
The purpose of this gem is to provide a __simple__ way to handle the __inheritance__ of __config files__ for a ruby
|
6
10
|
script. By default, it will handle already few config layers:
|
7
11
|
|
8
12
|
* The __system layer__, which is a level common to all applications using this gem.
|
9
13
|
* The __global layer__, which is the level to declare options for all users that use the ruby script using this gem.
|
10
14
|
* The __user layer__, which is the level, where a user can set options for the ruby script using this gem.
|
11
|
-
* The __extra layer__, which provides the possibility to specify
|
15
|
+
* The __extra layer__, which provides the possibility to specify another config file from the command line.
|
12
16
|
* The __command-line layer__, which provides the ability to specify options from the command line.
|
13
17
|
* The __override layer__, which will contain all modifications done to the config at run time.
|
14
18
|
|
15
19
|
The different layers are evaluated by default in that order using the [super_stack gem][SS] (Please read for more
|
16
|
-
detail).
|
20
|
+
detail). The `StackedConfig::Orchestrator` will expose a merged view of all its layers without modifying them.
|
17
21
|
|
18
22
|
All the config files are following the [YAML] syntax.
|
19
23
|
|
@@ -54,9 +58,25 @@ if config[:help]
|
|
54
58
|
end
|
55
59
|
```
|
56
60
|
|
57
|
-
Try this little script and then create some config files to test how it is handled (see next section
|
58
|
-
create the config files).
|
61
|
+
Try this little script and then create some config files to test how it is handled (see [next section]
|
62
|
+
(#where-are-my-config-files-) to know where to create the config files).
|
63
|
+
|
64
|
+
You are supposed to access the merged config through the `[]` method of the orchestrator (`config[]` in the previous
|
65
|
+
example). Still you can directly modify any of the layers automatically created, you are not supposed to, and if you
|
66
|
+
modify or add any new property (a so called "runtime property"), it will actually set the property in the `override`
|
67
|
+
layer.
|
68
|
+
|
69
|
+
That's why you can always come back to the initial state by calling `reset` on the orchestrator. This actually just
|
70
|
+
clears the override layer.
|
59
71
|
|
72
|
+
Every layer is accessible through the following orchestrator properties:
|
73
|
+
|
74
|
+
* `system_layer`
|
75
|
+
* `global_layer`
|
76
|
+
* `user_layer`
|
77
|
+
* `provided_config_file_layer`
|
78
|
+
* `command_line_layer`
|
79
|
+
* `write_layer`
|
60
80
|
|
61
81
|
### Where are my config files ?
|
62
82
|
|
@@ -72,14 +92,15 @@ As you can see in the sources, paths are expressed using kind of 'templates', wh
|
|
72
92
|
|
73
93
|
* `##SYSTEM_CONFIG_ROOT##` is where the system config is stored. On Unix systems, it should be `/etc`.
|
74
94
|
* `##PROGRAM_NAME##` is by default the name of the script you are running (with no extension). You can if you want
|
75
|
-
change this name at runtime.
|
95
|
+
change this name at runtime. __Changing it (using the `executable_name` orchestrator property ) will trigger a
|
96
|
+
re-search and reload of all the config files__.
|
76
97
|
* `##USER_CONFIG_ROOT##` is where the user config is stored. On Unix systems, it should be your `$HOME` directory.
|
77
98
|
* `##EXTENSION##` is one of the following extensions : `conf CONF cfg CFG yml YML yaml YAML`.
|
78
99
|
|
79
100
|
The search of the config files is done according to the order defined in sources just above and then extensions
|
80
101
|
are tried according to the extensions just above in that exact order.
|
81
102
|
|
82
|
-
__The first file matching for a particular level is used !
|
103
|
+
__The first file matching for a particular level is used ! And there can be only one per level.__
|
83
104
|
|
84
105
|
Thus according to the rules above, and assuming my script is named `my_script.rb` if the two following files exists at
|
85
106
|
user config level, only the first is taken in account:
|
@@ -90,11 +111,98 @@ user config level, only the first is taken in account:
|
|
90
111
|
|
91
112
|
### Script command line options
|
92
113
|
|
93
|
-
`stacked_config` uses internally the fantastic [Slop] gem to manage options coming from
|
94
|
-
|
114
|
+
`stacked_config` uses internally the fantastic [Slop] gem to manage options coming from the command line within the
|
115
|
+
command line layer. This layer will be simply part of the complete config that the orchestrator exposes.
|
116
|
+
|
117
|
+
#### Command line help
|
118
|
+
|
119
|
+
You can easily display a help using the orchestrator `command_line_help` method.
|
120
|
+
|
121
|
+
To even have a better command line help displayed you can provide optional information:
|
122
|
+
|
123
|
+
* The __application name__ through the `app_name` orchestrator property.
|
124
|
+
* The __application version__ through the `app_version` orchestrator property.
|
125
|
+
* The __application description__ through the `app_description` orchestrator property.
|
126
|
+
|
127
|
+
You could as well do this with the `describes_application` method (see [complete example]()).
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
require 'stacked_config'
|
131
|
+
|
132
|
+
config = StackedConfig::Orchestrator.new
|
133
|
+
|
134
|
+
config.app_name = 'My super Application'
|
135
|
+
config.app_version = '1.0.0'
|
136
|
+
config.app_description = <<EOD
|
137
|
+
You can have a multiline description of your application.
|
138
|
+
This may help a lot having a consistent command-line help.
|
139
|
+
EOD
|
140
|
+
puts config.command_line_help
|
141
|
+
```
|
142
|
+
|
143
|
+
This would issue:
|
144
|
+
|
145
|
+
```
|
146
|
+
Usage: myscript [options]
|
147
|
+
My super Application Version: 1.0.0
|
148
|
+
|
149
|
+
You can have a multiline description of your application.
|
150
|
+
This may help a lot having a consistent command-line help.
|
151
|
+
|
152
|
+
-- Generic options -------------------------------------------------------------
|
153
|
+
--auto Auto mode. Bypasses questions to user.
|
154
|
+
--simulate Do not perform the actual underlying actions.
|
155
|
+
-v, --verbose Enable verbose mode.
|
156
|
+
-h, --help Displays this help.
|
157
|
+
-- Configuration options -------------------------------------------------------
|
158
|
+
--config-file Specify a config file.
|
159
|
+
--config-override If specified override all other config.
|
160
|
+
```
|
161
|
+
|
162
|
+
Which are the default options available.
|
163
|
+
|
164
|
+
|
165
|
+
#### Default command line options
|
166
|
+
|
167
|
+
By default the following command line options are available:
|
168
|
+
|
169
|
+
* __auto__ Auto mode. Bypasses questions to user. Just provided for convenience. Not used anywhere in the
|
170
|
+
code.
|
171
|
+
* __simulate__ Do not perform the actual underlying actions. Just provided for convenience. Not used anywhere
|
172
|
+
in the code.
|
173
|
+
* __verbose__ Enable verbose mode. Just provided for convenience. Not used anywhere in the code.
|
174
|
+
* __help__ Displays this help. Just provided for convenience. Not used anywhere in the code.
|
175
|
+
* __config-file__ Specify a config file for the extra layer.
|
176
|
+
* __config-override__ If specified, means that the config file specified will override all other config (actually
|
177
|
+
changes the merge policy [see below] (#changing-the-way-things-are-merged)) of the extra layer.
|
178
|
+
|
179
|
+
Flags that are said "Just provided for convenience. Not used anywhere in the code." are just there because they are
|
180
|
+
standard options and thus you can easily test in your code.
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
puts "Something very important" if config[:verbose]
|
184
|
+
```
|
185
|
+
|
186
|
+
`stacked_config` provides a convenient method to display the command line help every user expects from a decent script:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
require 'stacked_config'
|
190
|
+
|
191
|
+
config = StackedConfig::Orchestrator.new
|
192
|
+
|
193
|
+
if config[:help]
|
194
|
+
# command_line_help will provide a formatted help to display on the command line
|
195
|
+
puts config.command_line_help
|
196
|
+
exit 0
|
197
|
+
end
|
198
|
+
|
199
|
+
# ... do something else
|
200
|
+
```
|
201
|
+
|
202
|
+
#### Adding new command line options
|
95
203
|
|
96
|
-
To define your options the command-line layer exposes a `slop_definition` method that enables
|
97
|
-
|
204
|
+
To define your options the command-line layer exposes a `slop_definition` method that enables to directly configure
|
205
|
+
slop.
|
98
206
|
|
99
207
|
For most usages you can use the higher level method `add_command_line_section` from the orchestrator.
|
100
208
|
|
@@ -110,20 +218,23 @@ end
|
|
110
218
|
|
111
219
|
The `add_command_line_section` method supports a parameter to define the name of the section.
|
112
220
|
|
221
|
+
__Of course adding new command line options will adapt the display of the `command_line_help` method of the
|
222
|
+
orchestrator__.
|
113
223
|
|
114
|
-
### Advanced usage
|
115
224
|
|
116
|
-
|
225
|
+
## Advanced usage
|
226
|
+
|
227
|
+
### Re-ordering layers
|
117
228
|
|
118
229
|
The way layers are processed is done according to their priority. By default the existing layers have the following
|
119
230
|
priorities:
|
120
231
|
|
121
|
-
* The system layer has a priority of
|
122
|
-
* The global layer has a priority of
|
123
|
-
* The user layer has a priority of
|
124
|
-
* The extra layer has a priority of
|
125
|
-
* The command-line layer has a priority of
|
126
|
-
* The override layer has a priority of
|
232
|
+
* The system layer has a priority of __10__
|
233
|
+
* The global layer has a priority of __20__
|
234
|
+
* The user layer has a priority of __30__
|
235
|
+
* The extra layer has a priority of __40__
|
236
|
+
* The command-line layer has a priority of __100__
|
237
|
+
* The override layer has a priority of __1000__
|
127
238
|
|
128
239
|
But imagine you want to say that no-one could override properties defined at the system and global layer even from the
|
129
240
|
command-line, then you just have to change the priorities of those 2 layers.
|
@@ -137,10 +248,10 @@ config.global_layer.priority = 1600
|
|
137
248
|
```
|
138
249
|
|
139
250
|
By doing such the system and global layers will be evaluated after the command line layer and therefore properties set
|
140
|
-
in those files cannot be overridden even at command line level.
|
251
|
+
in those files cannot be overridden even at command line level thanks to [super_stack][SS] mechanisms.
|
141
252
|
|
142
253
|
|
143
|
-
|
254
|
+
### Adding extra layers
|
144
255
|
|
145
256
|
Imagine you want to add a specific layer in your config, coming from let's say a web-service or a database, you may
|
146
257
|
create your own layers for this purpose. Have a look at [super_stack gem][SS] for further info about how to create
|
@@ -148,14 +259,165 @@ layers.
|
|
148
259
|
|
149
260
|
But basically just create your new layer, gives it a priority and add it to the orchestrator.
|
150
261
|
|
262
|
+
### Changing the way things are merged
|
263
|
+
|
264
|
+
The [super_stack gem][SS] defines some different merge policies. By default `stacked_config` will use the
|
265
|
+
`SuperStack::MergePolicies::FullMergePolicy` that merges hashes and arrays at all levels. But you can choose to completely
|
266
|
+
change the merge behaviour by changing the merge policy. See [super_stack gem][SS] documentation for other merge
|
267
|
+
policies.
|
268
|
+
|
269
|
+
Merge policies can be changed either at orchestrator level or at layer level by setting the merge_policy property.
|
270
|
+
|
271
|
+
This is actually exactly what happens when the `config-override` flag is passed on the command line. It triggers the
|
272
|
+
change of the merge policy of the extra layer from `SuperStack::MergePolicies::FullMergePolicy` to
|
273
|
+
`SuperStack::MergePolicies::OverridePolicy`.
|
274
|
+
|
275
|
+
## A complete example of a program using `stacked_config`
|
276
|
+
|
277
|
+
Save the following file somewhere as `example.rb`.
|
278
|
+
|
279
|
+
You can use this as a template for your own scripts:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
#!/usr/bin/env ruby
|
283
|
+
|
284
|
+
require 'stacked_config'
|
285
|
+
|
286
|
+
class MyApp
|
287
|
+
|
288
|
+
VERSION = '0.0.1'
|
289
|
+
NAME = 'My brand new Application'
|
290
|
+
DESCRIPTION = 'Best app ever'
|
291
|
+
|
292
|
+
attr_reader :config
|
293
|
+
|
294
|
+
def initialize
|
295
|
+
@config = StackedConfig::Orchestrator.new
|
296
|
+
config.describes_application app_name: NAME, app_version: VERSION, app_description: DESCRIPTION
|
297
|
+
add_script_options
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
def add_script_options
|
302
|
+
config.add_command_line_section('Options for the script') do |slop|
|
303
|
+
slop.on :u, :useless, 'Stupid option', :argument => false
|
304
|
+
slop.on :an_int, 'Stupid option with integer argument', :argument => true, :as => Integer
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def run
|
309
|
+
if config[:help]
|
310
|
+
puts config.command_line_help
|
311
|
+
exit 0
|
312
|
+
end
|
313
|
+
do_some_processing
|
314
|
+
end
|
315
|
+
|
316
|
+
def do_some_processing
|
317
|
+
# Here you would really start your process
|
318
|
+
config[:something] = 'Added something...'
|
319
|
+
|
320
|
+
if config[:verbose]
|
321
|
+
puts ' ## Here is a display of the config sources and contents'
|
322
|
+
puts config.detailed_layers_info
|
323
|
+
puts ' ## This the resulting merged config'
|
324
|
+
puts config[].to_yaml
|
325
|
+
end
|
326
|
+
|
327
|
+
puts ' ## Bye...'
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
MyApp.new.run
|
333
|
+
```
|
334
|
+
|
335
|
+
If you run
|
336
|
+
|
337
|
+
$ ./example.rb
|
338
|
+
|
339
|
+
You would get
|
340
|
+
|
341
|
+
```
|
342
|
+
## Bye...
|
343
|
+
```
|
344
|
+
|
345
|
+
If you run
|
346
|
+
|
347
|
+
$ ./example.rb --help
|
348
|
+
|
349
|
+
You would get
|
350
|
+
|
351
|
+
```
|
352
|
+
Usage: example [options]
|
353
|
+
My brand new Application Version: 0.0.1
|
354
|
+
|
355
|
+
Best app ever
|
356
|
+
-- Generic options -------------------------------------------------------------
|
357
|
+
--auto Auto mode. Bypasses questions to user.
|
358
|
+
--simulate Do not perform the actual underlying actions.
|
359
|
+
-v, --verbose Enable verbose mode.
|
360
|
+
-h, --help Displays this help.
|
361
|
+
-- Configuration options -------------------------------------------------------
|
362
|
+
--config-file Specify a config file.
|
363
|
+
--config-override If specified override all other config.
|
364
|
+
-- Options for the script ------------------------------------------------------
|
365
|
+
-u, --useless Stupid option
|
366
|
+
--an_int Stupid option with integer argument
|
367
|
+
```
|
368
|
+
|
369
|
+
If you run
|
370
|
+
|
371
|
+
$ ./example.rb --verbose
|
372
|
+
|
373
|
+
You would get
|
374
|
+
```
|
375
|
+
## Here is a display of the config sources and contents
|
376
|
+
--------------------------------------------------------------------------------
|
377
|
+
System-wide configuration level
|
378
|
+
There is no file attached to this level.
|
379
|
+
There is no data in this layer
|
380
|
+
--------------------------------------------------------------------------------
|
381
|
+
Global configuration level
|
382
|
+
There is no file attached to this level.
|
383
|
+
There is no data in this layer
|
384
|
+
--------------------------------------------------------------------------------
|
385
|
+
User configuration level
|
386
|
+
There is no file attached to this level.
|
387
|
+
There is no data in this layer
|
388
|
+
--------------------------------------------------------------------------------
|
389
|
+
Specific config file configuration level
|
390
|
+
There is no file attached to this level.
|
391
|
+
There is no data in this layer
|
392
|
+
--------------------------------------------------------------------------------
|
393
|
+
Command line configuration level
|
394
|
+
There is no file attached to this level.
|
395
|
+
This layer contains the following data:
|
396
|
+
--- !ruby/hash:StackedConfig::Layers::CommandLineLayer
|
397
|
+
:verbose: true
|
398
|
+
|
399
|
+
--------------------------------------------------------------------------------
|
400
|
+
Overridden configuration level
|
401
|
+
There is no file attached to this level.
|
402
|
+
This layer contains the following data:
|
403
|
+
--- !ruby/hash:SuperStack::Layer
|
404
|
+
:something: Added something...
|
405
|
+
|
406
|
+
--------------------------------------------------------------------------------
|
407
|
+
## This the resulting merged config
|
408
|
+
---
|
409
|
+
:verbose: true
|
410
|
+
:something: Added something...
|
411
|
+
## Bye...
|
412
|
+
```
|
151
413
|
|
152
414
|
## Contributing
|
153
415
|
|
154
|
-
1. [Fork it] ( https://github.com/lbriais/stacked_config/fork )
|
155
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
156
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
157
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
158
|
-
5. Create
|
416
|
+
1. [Fork it] ( https://github.com/lbriais/stacked_config/fork ), clone your fork.
|
417
|
+
2. Create your feature branch (`git checkout -b my-new-feature`) and develop your super extra feature.
|
418
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
419
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
420
|
+
5. Create a Pull Request.
|
159
421
|
|
160
422
|
[SS]: https://github.com/lbriais/super_stack "Super Stack gem"
|
161
423
|
[SystemLayer]: https://github.com/lbriais/stacked_config/blob/master/lib/stacked_config/layers/system_layer.rb "the system layer places where config files are searched"
|
@@ -14,30 +14,6 @@ module StackedConfig
|
|
14
14
|
describes_application executable_name: default_name, app_name: default_name
|
15
15
|
end
|
16
16
|
|
17
|
-
def detailed_layers_info
|
18
|
-
info, sep = [], '-' * 80
|
19
|
-
info << sep
|
20
|
-
layers.values.sort {|a, b| a.priority <=> b.priority}.each do |layer|
|
21
|
-
info << layer.name
|
22
|
-
if layer.file_name.nil?
|
23
|
-
info << 'There is no file attached to this level.'
|
24
|
-
else
|
25
|
-
info << "Using '#{layer.file_name}' as config file for this layer."
|
26
|
-
end
|
27
|
-
if layer.empty?
|
28
|
-
info << 'There is no data in this layer'
|
29
|
-
else
|
30
|
-
info << 'This layer contains the following data:'
|
31
|
-
info << layer.to_yaml
|
32
|
-
end
|
33
|
-
info << sep
|
34
|
-
end
|
35
|
-
info.join "\n"
|
36
|
-
end
|
37
|
-
|
38
|
-
def command_line_help
|
39
|
-
command_line_layer.help
|
40
|
-
end
|
41
17
|
|
42
18
|
def self.default_executable_name
|
43
19
|
File.basename($PROGRAM_NAME).gsub /\.[^\.]+$/, ''
|
@@ -42,6 +42,32 @@ module StackedConfig
|
|
42
42
|
|
43
43
|
end
|
44
44
|
|
45
|
+
|
46
|
+
def detailed_layers_info
|
47
|
+
info, sep = [], '-' * 80
|
48
|
+
info << sep
|
49
|
+
layers.values.sort {|a, b| a.priority <=> b.priority}.each do |layer|
|
50
|
+
info << layer.name
|
51
|
+
if layer.file_name.nil?
|
52
|
+
info << 'There is no file attached to this level.'
|
53
|
+
else
|
54
|
+
info << "Using '#{layer.file_name}' as config file for this layer."
|
55
|
+
end
|
56
|
+
if layer.empty?
|
57
|
+
info << 'There is no data in this layer'
|
58
|
+
else
|
59
|
+
info << 'This layer contains the following data:'
|
60
|
+
info << layer.to_yaml
|
61
|
+
end
|
62
|
+
info << sep
|
63
|
+
end
|
64
|
+
info.join "\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
def command_line_help
|
68
|
+
command_line_layer.help
|
69
|
+
end
|
70
|
+
|
45
71
|
def describes_application(options = {})
|
46
72
|
self.app_name = options.fetch(:app_name, nil)
|
47
73
|
self.app_version = options.fetch(:app_version, nil)
|