stacked_config 0.1.1 → 0.1.2
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 +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
|
[](https://travis-ci.org/lbriais/stacked_config)
|
3
3
|
[](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)
|