tty-config 0.3.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +305 -153
  5. data/lib/tty/config/dependency_loader.rb +55 -0
  6. data/lib/tty/config/generator.rb +57 -0
  7. data/lib/tty/config/marshaller.rb +66 -0
  8. data/lib/tty/config/marshaller_registry.rb +45 -0
  9. data/lib/tty/config/marshallers/hcl_marshaller.rb +30 -0
  10. data/lib/tty/config/marshallers/ini_marshaller.rb +31 -0
  11. data/lib/tty/config/marshallers/java_props_marshaller.rb +28 -0
  12. data/lib/tty/config/marshallers/json_marshaller.rb +28 -0
  13. data/lib/tty/config/marshallers/toml_marshaller.rb +28 -0
  14. data/lib/tty/config/marshallers/yaml_marshaller.rb +32 -0
  15. data/lib/tty/config/marshallers.rb +35 -0
  16. data/lib/tty/config/version.rb +3 -3
  17. data/lib/tty/config.rb +374 -190
  18. data/lib/tty-config.rb +1 -1
  19. metadata +66 -57
  20. data/Rakefile +0 -8
  21. data/bin/console +0 -14
  22. data/bin/setup +0 -8
  23. data/spec/spec_helper.rb +0 -54
  24. data/spec/unit/alias_setting_spec.rb +0 -72
  25. data/spec/unit/append_spec.rb +0 -26
  26. data/spec/unit/autoload_env_spec.rb +0 -62
  27. data/spec/unit/delete_spec.rb +0 -22
  28. data/spec/unit/exist_spec.rb +0 -24
  29. data/spec/unit/fetch_spec.rb +0 -45
  30. data/spec/unit/generate_spec.rb +0 -70
  31. data/spec/unit/merge_spec.rb +0 -13
  32. data/spec/unit/new_spec.rb +0 -6
  33. data/spec/unit/normalize_hash_spec.rb +0 -21
  34. data/spec/unit/read_spec.rb +0 -109
  35. data/spec/unit/remove_spec.rb +0 -16
  36. data/spec/unit/set_from_env_spec.rb +0 -78
  37. data/spec/unit/set_if_empty_spec.rb +0 -26
  38. data/spec/unit/set_spec.rb +0 -62
  39. data/spec/unit/validate_spec.rb +0 -76
  40. data/spec/unit/write_spec.rb +0 -197
  41. data/tasks/console.rake +0 -11
  42. data/tasks/coverage.rake +0 -11
  43. data/tasks/spec.rake +0 -29
  44. data/tty-config.gemspec +0 -30
data/README.md CHANGED
@@ -1,42 +1,46 @@
1
1
  <div align="center">
2
- <a href="https://piotrmurach.github.io/tty" target="_blank"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="tty logo" /></a>
2
+ <a href="https://ttytoolkit.org"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="TTY Toolkit logo" /></a>
3
3
  </div>
4
4
 
5
5
  # TTY::Config [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/tty-config.svg)][gem]
8
- [![Build Status](https://secure.travis-ci.org/piotrmurach/tty-config.svg?branch=master)][travis]
8
+ [![Actions CI](https://github.com/piotrmurach/tty-config/workflows/CI/badge.svg?branch=master)][gh_actions_ci]
9
9
  [![Build status](https://ci.appveyor.com/api/projects/status/2383i0dn3hlw9cnn?svg=true)][appveyor]
10
10
  [![Maintainability](https://api.codeclimate.com/v1/badges/dfac05073e1549e9dbb6/maintainability)][codeclimate]
11
11
  [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-config/badge.svg)][coverage]
12
- [![Inline docs](http://inch-ci.org/github/piotrmurach/tty-config.svg?branch=master)][inchpages]
12
+ [![Inline docs](https://inch-ci.org/github/piotrmurach/tty-config.svg?branch=master)][inchpages]
13
13
 
14
14
  [gitter]: https://gitter.im/piotrmurach/tty
15
- [gem]: http://badge.fury.io/rb/tty-config
16
- [travis]: http://travis-ci.org/piotrmurach/tty-config
15
+ [gem]: https://badge.fury.io/rb/tty-config
16
+ [gh_actions_ci]: https://github.com/piotrmurach/tty-config/actions?query=workflow%3ACI
17
17
  [appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-config
18
18
  [codeclimate]: https://codeclimate.com/github/piotrmurach/tty-config/maintainability
19
19
  [coverage]: https://coveralls.io/github/piotrmurach/tty-config
20
- [inchpages]: http://inch-ci.org/github/piotrmurach/tty-config
20
+ [inchpages]: https://inch-ci.org/github/piotrmurach/tty-config
21
21
 
22
- > Define, read and write any Ruby app configurations with a penchant for terminal clients.
22
+ > A highly customisable application configuration interface for building terminal tools.
23
23
 
24
- **TTY::Config** provides app configuration component for [TTY](https://github.com/piotrmurach/tty) toolkit.
24
+ **TTY::Config** provides application configuration component for [TTY](https://github.com/piotrmurach/tty) toolkit.
25
25
 
26
26
  ## Features
27
27
 
28
- * Read & write configurations in YAML, JSON, TOML, INI formats
29
- * Simple interface for adding and reading settings for deeply nested keys
30
- * Indifferent access for reading settings
31
- * Merging of configuration settings from other hashes
32
- * Reading values from environment variables
28
+ This is a one-stop shop for all your configuration needs:
29
+
30
+ * [Read](#216-read) and [write](#217-write) config files in YAML, JSON, TOML, INI, HCL and Java Properties formats
31
+ * Add [custom marshallers](#222-register_marshaller) or override the built-in ones
32
+ * [Set](#21-set) and [read](#24-fetch) settings for deeply nested keys
33
+ * [Set](#21-set) defaults for undefined settings
34
+ * [Read](#24-fetch) settings with indifferent access
35
+ * [Merge](#25-merge) configuration settings from other hash objects
36
+ * Read values from [environment variables](#23-set_from_env)
33
37
 
34
38
  ## Installation
35
39
 
36
40
  Add this line to your application's Gemfile:
37
41
 
38
42
  ```ruby
39
- gem 'tty-config'
43
+ gem "tty-config"
40
44
  ```
41
45
 
42
46
  And then execute:
@@ -63,15 +67,18 @@ Or install it yourself as:
63
67
  * [2.9 delete](#29-delete)
64
68
  * [2.10 alias_setting](#210-alias_setting)
65
69
  * [2.11 validate](#211-validate)
66
- * [2.12 env_prefix=](#212-env_prefix)
67
- * [2.13 filename=](#213-filename)
68
- * [2.14 extname=](#214-extname)
69
- * [2.15 append_path](#215-append_path)
70
- * [2.16 prepend_path](#216-prepend_path)
71
- * [2.17 read](#217-read)
72
- * [2.18 write](#218-write)
73
- * [2.19 exist?](#219-exist)
74
- * [2.20 autoload_env](#220-autoload_env)
70
+ * [2.12 filename=](#212-filename)
71
+ * [2.13 extname=](#213-extname)
72
+ * [2.14 append_path](#214-append_path)
73
+ * [2.15 prepend_path](#215-prepend_path)
74
+ * [2.16 read](#216-read)
75
+ * [2.17 write](#217-write)
76
+ * [2.18 exist?](#218-exist)
77
+ * [2.19 env_prefix=](#219-env_prefix)
78
+ * [2.20 env_separator=](#220-env_separator)
79
+ * [2.21 autoload_env](#221-autoload_env)
80
+ * [2.22 register_marshaller](#222-register_marshaller)
81
+ * [2.23 unregister_marshaller](#223-unregister_marshaller)
75
82
  * [3. Examples](#3-examples)
76
83
  * [3.1 Working with env vars](#31-working-with-env-vars)
77
84
  * [3.2 Working with optparse](#32-working-with-optparse)
@@ -82,30 +89,30 @@ Initialize the configuration and provide the name:
82
89
 
83
90
  ```ruby
84
91
  config = TTY::Config.new
85
- config.filename = 'investments'
92
+ config.filename = "investments"
86
93
  ```
87
94
 
88
- then configure values for different nested keys with `set` and `append`:
95
+ Then configure values for different nested keys with `set` and `append`:
89
96
 
90
97
  ```ruby
91
- config.set(:settings, :base, value: 'USD')
98
+ config.set(:settings, :base, value: "USD")
92
99
  config.set(:settings, :color, value: true)
93
- config.set(:coins, value: ['BTC'])
100
+ config.set(:coins, value: ["BTC"])
94
101
 
95
- config.append('ETH', 'TRX', 'DASH', to: :coins)
102
+ config.append("ETH", "TRX", "DASH", to: :coins)
96
103
  ```
97
104
 
98
- get any value by using `fetch`:
105
+ You can get any value by using `fetch`:
99
106
 
100
107
  ```ruby
101
108
  config.fetch(:settings, :base)
102
- # => 'USD'
109
+ # => "USD"
103
110
 
104
111
  config.fetch(:coins)
105
- # => ['BTC', 'ETH', 'TRX', 'DASH']
112
+ # => ["BTC", "ETH", "TRX", "DASH"]
106
113
  ```
107
114
 
108
- and `write` configration out to `investments.yml`:
115
+ And call `write` to persist the configuration to `investments.yml` file:
109
116
 
110
117
  ```ruby
111
118
  config.write
@@ -121,14 +128,14 @@ config.write
121
128
  # - DASH
122
129
  ```
123
130
 
124
- and then to read an `investments.yml` file, you need to provide the locations to search in:
131
+ To read an `investments.yml` file, you need to provide the locations to search in:
125
132
 
126
133
  ```ruby
127
134
  config.append_path Dir.pwd
128
135
  config.append_path Dir.home
129
136
  ```
130
137
 
131
- Finally, read in configuration back again:
138
+ Finally, call `read` to convert configuration file back into an object again:
132
139
 
133
140
  ```ruby
134
141
  config.read
@@ -144,8 +151,8 @@ class App
144
151
 
145
152
  def initialize
146
153
  @config = TTY::Config.new
147
- @config.filename = 'investments'
148
- @config.extname = '.toml'
154
+ @config.filename = "investments"
155
+ @config.extname = ".toml"
149
156
  @config.append_path Dir.pwd
150
157
  @config.append_path Dir.home
151
158
  end
@@ -163,22 +170,22 @@ end
163
170
  To set configuration setting use `set` method. It accepts any number of keys and value by either using `:value` keyword argument or passing a block:
164
171
 
165
172
  ```ruby
166
- config.set(:base, value: 'USD')
167
- config.set(:base) { 'USD' }
173
+ config.set(:base, value: "USD")
174
+ config.set(:base) { "USD" }
168
175
  ```
169
176
 
170
- The block version of specifying a value will mean that the value is evaluated every time its being read.
177
+ The block version of specifying a value will mean that the value is evaluated every time it's being read.
171
178
 
172
179
  You can also specify deeply nested configuration settings by passing sequence of keys:
173
180
 
174
181
  ```ruby
175
- config.set :settings, :base, value: 'USD'
182
+ config.set(:settings, :base, value: "USD")
176
183
  ```
177
184
 
178
- is equivalent to:
185
+ Which is equivalent to:
179
186
 
180
187
  ```ruby
181
- config.set 'settings.base', value: 'USD'
188
+ config.set("settings.base", value: "USD")
182
189
  ```
183
190
 
184
191
  Internally all configuration settings are stored as string keys for ease of working with configuration files and command line application's inputs.
@@ -188,13 +195,13 @@ Internally all configuration settings are stored as string keys for ease of work
188
195
  To set a configuration setting only if it hasn't been set before use `set_if_empty`:
189
196
 
190
197
  ```ruby
191
- config.set_if_empty :base, value: 'USD'
198
+ config.set_if_empty(:base, value: "USD")
192
199
  ```
193
200
 
194
201
  Similar to `set` it allows you to specify arbitrary sequence of keys followed by a key value or block:
195
202
 
196
203
  ```ruby
197
- config.set_if_empty :settings, :base, value: 'USD'
204
+ config.set_if_empty(:settings, :base, value: "USD")
198
205
  ```
199
206
 
200
207
  ### 2.3 set_from_env
@@ -204,8 +211,8 @@ To read configuration options from environment variables use `set_from_env`. At
204
211
  Given the following environment variables:
205
212
 
206
213
  ```ruby
207
- ENV['HOST'] = '192.168.1.17'
208
- ENV['PORT'] = '7727'
214
+ ENV["HOST"] = "192.168.1.17"
215
+ ENV["PORT"] = "7727"
209
216
  ```
210
217
 
211
218
  You can make the config aware of the above env variables:
@@ -219,35 +226,35 @@ Then you can retrieve values like any other configuration option:
219
226
 
220
227
  ```ruby
221
228
  config.fetch(:host)
222
- # => '192.168.1.17'
229
+ # => "192.168.1.17"
223
230
  config.fetch(:port)
224
- # => '7727'
231
+ # => "7727"
225
232
  ```
226
233
 
227
234
  If you want the configuration key name to be different from `ENV` variable name use a block:
228
235
 
229
236
  ```ruby
230
- config.set_from_env(:host) { 'HOSTNAME' }
237
+ config.set_from_env(:host) { "HOSTNAME" }
231
238
  config.set_from_env(:host) { :hostname }
232
239
  ```
233
240
 
234
241
  You can also configure settings for deeply nested keys:
235
242
 
236
243
  ```ruby
237
- config.set_from_env(:settings, :base) { 'CURRENCY' }
244
+ config.set_from_env(:settings, :base) { "CURRENCY" }
238
245
  config.set_from_env(:settings, :base) { :currency }
239
- config.set_from_env('settings.base') { 'CURRENCY'}
240
- config.set_from_env('settings.base') { :currency}
246
+ config.set_from_env("settings.base") { "CURRENCY" }
247
+ config.set_from_env("settings.base") { :currency }
241
248
  ```
242
249
 
243
- And asssuming `ENV['CURRENCY']=USD`:
250
+ And assuming `ENV["CURRENCY"]=USD`:
244
251
 
245
252
  ```ruby
246
253
  config.fetch(:settings, :base)
247
254
  # => USD
248
255
  ```
249
256
 
250
- You can also prefix your environment variables. See [env_prefix=](#212-env_prefix)
257
+ You can also prefix your environment variables with [env_prefix=](#219-env_prefix) or use a different separator with [env_separator](#220-env_separator).
251
258
 
252
259
  It's important to recognise that `set_from_env` doesn't record the value for the environment variables. They are read each time from the `ENV` when `fetch` is called.
253
260
 
@@ -256,8 +263,8 @@ It's important to recognise that `set_from_env` doesn't record the value for the
256
263
  To get a configuration setting use `fetch`, which can accept default value either with a `:default` keyword or a block that will be lazy evaluated:
257
264
 
258
265
  ```ruby
259
- config.fetch(:base, default: 'USD')
260
- config.fetch(:base) { 'USD' }
266
+ config.fetch(:base, default: "USD")
267
+ config.fetch(:base) { "USD" }
261
268
  ```
262
269
 
263
270
  Similar to `set` operation, `fetch` allows you to retrieve deeply nested values:
@@ -266,19 +273,19 @@ Similar to `set` operation, `fetch` allows you to retrieve deeply nested values:
266
273
  config.fetch(:settings, :base) # => USD
267
274
  ```
268
275
 
269
- is equivalent to:
276
+ Which is equivalent to:
270
277
 
271
278
  ```ruby
272
- config.fetch('settings.base')
279
+ config.fetch("settings.base")
273
280
  ```
274
281
 
275
282
  `fetch` has indifferent access so you can mix string and symbol keys, all the following examples retrieve the value:
276
283
 
277
284
  ```ruby
278
285
  config.fetch(:settings, :base)
279
- config.fetch('settings', 'base')
280
- config.fetch(:settings', 'base')
281
- config.fetch('settings', :base)
286
+ config.fetch("settings", "base")
287
+ config.fetch(:settings, "base")
288
+ config.fetch("settings", :base)
282
289
  ```
283
290
 
284
291
  ### 2.5 merge
@@ -289,7 +296,7 @@ To merge in other configuration settings as hash use `merge`:
289
296
  config.set(:a, :b, value: 1)
290
297
  config.set(:a, :c, value: 2)
291
298
 
292
- config.merge({'a' => {'c' => 3, 'd' => 4}})
299
+ config.merge({"a" => {"c" => 3, "d" => 4}})
293
300
 
294
301
  config.fetch(:a, :c) # => 3
295
302
  config.fetch(:a, :d) # => 4
@@ -373,6 +380,14 @@ config.delete(:settings, :base)
373
380
  # "USD"
374
381
  ```
375
382
 
383
+ You can provide an optional default value in a block that will be returned when a key is not set:
384
+
385
+ ```ruby
386
+ config.delete(:settings, :unknown) { |key| "#{key} isn't set" }
387
+ # =>
388
+ # "unknown isn't set"
389
+ ```
390
+
376
391
  ### 2.10 alias_setting
377
392
 
378
393
  In order to alias a configuration setting to another name use `alias_setting`.
@@ -380,7 +395,7 @@ In order to alias a configuration setting to another name use `alias_setting`.
380
395
  For example, given an already existing setting:
381
396
 
382
397
  ```ruby
383
- config.set(:base, value: 'baz')
398
+ config.set(:base, value: "baz")
384
399
  ```
385
400
 
386
401
  You can alias it to another name:
@@ -393,29 +408,29 @@ And then access like any other configuration setting:
393
408
 
394
409
  ```ruby
395
410
  config.fetch(:currency)
396
- # => 'USD'
411
+ # => "USD"
397
412
  ```
398
413
 
399
414
  Deep nested configuration options are also supported:
400
415
 
401
416
  ```ruby
402
- config.set(:settings, :base, value: 'USD')
417
+ config.set(:settings, :base, value: "USD")
403
418
  ```
404
419
 
405
420
  And then can be aliased like so:
406
421
 
407
422
  ```ruby
408
423
  config.alias_setting(:settings, :base, to: [:settings, :currency])
409
- config.alias_setting('settings.base', to [:settings, :currency])
424
+ config.alias_setting("settings.base", to [:settings, :currency])
410
425
  ```
411
426
 
412
427
  You can then access the deep nested settings:
413
428
 
414
429
  ```ruby
415
430
  config.fetch(:settings, :currency)
416
- # => 'USD'
417
- config.fetch('settings.currency')
418
- # => 'USD'
431
+ # => "USD"
432
+ config.fetch("settings.currency")
433
+ # => "USD"
419
434
  ```
420
435
 
421
436
  ### 2.11 validate
@@ -432,71 +447,40 @@ end
432
447
 
433
448
  You can assign multiple validations for a given key and each of them will be run in the order they were registered when checking a value.
434
449
 
435
- When setting value all the validaitons will be run:
450
+ When setting value all the validations will be run:
436
451
 
437
452
  ```ruby
438
- config.set(:settings, :base, value: 'PL')
439
- # raises TTY::Config::ValidationError, 'Currency code needs to be 3 chars long.'
453
+ config.set(:settings, :base, value: "PL")
454
+ # raises TTY::Config::ValidationError, "Currency code needs to be 3 chars long."
440
455
  ```
441
456
 
442
- If the value s provided as a proc or a block then the validation will be delayed until the value is actually read:
457
+ If the value is provided as a proc or a block then the validation will be delayed until the value is actually read:
443
458
 
444
459
  ```ruby
445
- config.set(:settings, :base) { 'PL' }
460
+ config.set(:settings, :base) { "PL" }
446
461
  config.fetch(:settings, :base)
447
- # raises TTY::Config::ValidationError, 'Currency code needs to be 3 chars long.'
448
- ```
449
-
450
- ### 2.12 env_prefix=
451
-
452
- Given the following variables:
453
-
454
- ```ruby
455
- ENV['MYTOOL_HOST'] = '192.168.1.17'
456
- ENV['MYTOOL_PORT'] = ' 7727'
457
- ```
458
-
459
- You can inform configuration about common prefix using `env_prefix`:
460
-
461
- ```ruby
462
- config.env_prefix = 'mytool'
462
+ # raises TTY::Config::ValidationError, "Currency code needs to be 3 chars long."
463
463
  ```
464
464
 
465
- Then set configuration key name to environment variable name:
466
-
467
- ```ruby
468
- config.set_from_env(:host)
469
- config.set_from_env(:port)
470
- ```
471
-
472
- And finally retrieve the value:
473
-
474
- ```ruby
475
- config.fetch(:host)
476
- #=> '192.168.1.17'
477
- config.fetch(:port)
478
- # => '7727'
479
- ```
480
-
481
- ### 2.13 filename=
465
+ ### 2.12 filename=
482
466
 
483
467
  By default, **TTY::Config** searches for `config` named configuration file. To change this use `filename=` method without the extension name:
484
468
 
485
469
  ```ruby
486
- config.filename = 'investments'
470
+ config.filename = "investments"
487
471
  ```
488
472
 
489
- Then any supported extensions will be search for such as `.yml`, `.json` and `.toml`.
473
+ Then any supported extensions will be searched for such as `.yml`, `.json` and `.toml`.
490
474
 
491
- ### 2.14 extname=
475
+ ### 2.13 extname=
492
476
 
493
- By default '.yml' extension is used to write configuration out to a file but you can change that with `extname=`:
477
+ By default ".yml" extension is used to write configuration out to a file but you can change that with `extname=`:
494
478
 
495
479
  ```ruby
496
- config.extname = '.toml'
480
+ config.extname = ".toml"
497
481
  ```
498
482
 
499
- ### 2.15 append_path
483
+ ### 2.14 append_path
500
484
 
501
485
  You need to tell the **TTY::Config** where to search for configuration files. To search multiple paths for a configuration file use `append_path` or `prepend_path` methods.
502
486
 
@@ -508,9 +492,9 @@ config.append_path(Dir.home) # look in user's home directory
508
492
  config.append_path(Dir.pwd) # look in current working directory
509
493
  ```
510
494
 
511
- None of these paths are required, but you should provide at least one path if you wish to read configuration file.
495
+ None of these paths are required, but you should provide at least one path if you wish to read a configuration file.
512
496
 
513
- ### 2.16 prepend_path
497
+ ### 2.15 prepend_path
514
498
 
515
499
  The `prepend_path` allows you to add configuration search paths that should be searched first.
516
500
 
@@ -519,7 +503,7 @@ config.append_path(Dir.pwd) # look in current working directory second
519
503
  config.prepend_path(Dir.home) # look in user's home directory first
520
504
  ```
521
505
 
522
- ### 2.17 read
506
+ ### 2.16 read
523
507
 
524
508
  There are two ways for reading configuration files and both use the `read` method. One attempts to guess extension and format of your data, the other allows you to request specific extension and format.
525
509
 
@@ -529,14 +513,16 @@ Currently the supported file formats are:
529
513
  * `json` for `.json` extension
530
514
  * `toml` for `.toml` extension
531
515
  * `ini` for `.ini`, `.cnf`, `.conf`, `.cfg`, `.cf extensions`
516
+ * `hcl` for `.hcl` extensions
517
+ * `jprops` for `.properties`, `.props`, `.prop` extensions
532
518
 
533
- Calling `read` without any arguments searches through provided locations to find configuration file and reads it. Therefore, you need to specify at least one search path that contains the configuration file together with actual filename. When filename is specifed then all known extensions will be tried.
519
+ Calling `read` without any arguments searches through provided locations to find configuration file and reads it. Therefore, you need to specify at least one search path that contains the configuration file together with actual filename. When filename is specified then all known extensions will be tried.
534
520
 
535
521
  For example, to find file called investments in the current directory do:
536
522
 
537
523
  ```ruby
538
524
  config.append_path(Dir.pwd) # look in current working directory
539
- config.filename = 'investments' # file to search for
525
+ config.filename = "investments" # file to search for
540
526
  ```
541
527
 
542
528
  Find and read the configuration file:
@@ -548,7 +534,7 @@ config.read
548
534
  You can also specify directly the file to read without setting up any search paths or filenames. If you specify a configuration with a known file extension, an appropriate format will be guessed, in this instance `TOML`:
549
535
 
550
536
  ```ruby
551
- config.read('./investments.toml')
537
+ config.read("./investments.toml")
552
538
  ```
553
539
 
554
540
  In cases where you wish to specify a custom file extension, you will need to also specify the file format to use.
@@ -556,40 +542,70 @@ In cases where you wish to specify a custom file extension, you will need to als
556
542
  For example, if you have a configuration file formatted using `YAML` notation with extension called `.config`, to read it do:
557
543
 
558
544
  ```ruby
559
- config.read('investments.config', format: :yaml)
545
+ config.read("investments.config", format: :yaml)
546
+ ```
547
+
548
+ ### 2.17 write
549
+
550
+ By default **TTY::Config**, persists configuration file in the current working directory with a `config.yml` name. However, you can change the default file name by specifying the `filename` and `extension` type:
551
+
552
+ ```ruby
553
+ config.filename = "investments"
554
+ config.extname = ".toml"
560
555
  ```
561
556
 
562
- ### 2.18 write
557
+ Now, by invoking `write` you will persist the current configuration to `investments.toml` file.
563
558
 
564
- By default **TTY::Config**, persists configuration file in the current working directory with a `config.yml` name. However, you can change that by specifying the filename and extension type:
559
+ ```ruby
560
+ config.write # writes "investments.toml" in the current directory
561
+ ```
562
+
563
+ To write the current configuration to a file in a custom location, you can specify a direct location path and filename as an argument:
565
564
 
566
565
  ```ruby
567
- config.filename = 'investments'
568
- config.extname = '.toml'
566
+ config.write("/custom/path/to/investments.toml")
567
+ # may raise an error if any of the path directories are missing
569
568
  ```
570
569
 
571
- To write current configuration to a file, you can either specified direct location path and filename:
570
+ Alternatively, if the filename doesn't need to change you can specify only a custom path using the `:path` keyword:
572
571
 
573
572
  ```ruby
574
- config.write('./investments.toml')
573
+ config.write(path: "/custom/path/to")
574
+ # may raise an error if any of the path directories are missing
575
575
  ```
576
576
 
577
- Or, specify location paths to be searched for already existing configuration to overwrite:
577
+ If the `/custom/path/to` doesn't exist an error will be raised. You can set the `:create` option to make any missing directories in the path:
578
578
 
579
579
  ```ruby
580
- config.append_path(Dir.pwd) # search current working directory
580
+ config.write("/custom/path/to/investments.toml", create: true)
581
+ config.write(path: "/custom/path/to", create: true)
582
+ ```
581
583
 
582
- config.write
584
+ When the `investments.toml` file already exists the `TTY::Config::WriteError` error will be raised.
585
+
586
+ To create a configuration file regardless of whether it exists or not, use `:force` flag:
587
+
588
+ ```ruby
589
+ config.write(force: true)
590
+ config.write("/custom/path/to/investments.toml", force: true)
591
+ config.write(path: "/custom/path/to", force: true)
592
+ ```
593
+
594
+ By default, only the current directory is searched. You can specify additional location paths to be searched for already existing configuration to overwrite:
595
+
596
+ ```ruby
597
+ config.append_path("/custom/path/to") # search in "/custom/path/to" for config file
583
598
  ```
584
599
 
585
- To create configuration file regardless whether it exists or not, use `:force` flag:
600
+ By setting the `:create` option to `true`, you can ensure that even when no path is found that has a configuration file, the first location will be used and all missing directories created.
601
+
602
+ To ensure that a configuration file is written no matter what, use both `:create` and `:force`:
586
603
 
587
604
  ```ruby
588
- config.write(force: true) # overwrite any found config file
589
- config.write('./investments.toml', force: true) # overwrite specific config file
605
+ config.write(create: true, force: true)
590
606
  ```
591
607
 
592
- ### 2.19 exist?
608
+ ### 2.18 exist?
593
609
 
594
610
  To check if a configuration file exists within the configured search paths use `exist?` method:
595
611
 
@@ -597,20 +613,79 @@ To check if a configuration file exists within the configured search paths use `
597
613
  config.exist? # => true
598
614
  ```
599
615
 
600
- ### 2.20 autoload_env
616
+ ### 2.19 env_prefix=
601
617
 
602
- The `autload_env` allows you to automatically read environment variables. In most cases you would combine it with [env_prefix=](#212-env_prefix) to only read a subset of variables. When using `autload_env`, anytime the `fetch` is called a corresponding enviornment variable will be checked.
618
+ Given the following variables:
603
619
 
604
- For example, given an evironment variable `MYTOOL_HOST` set to `localhost`:
620
+ ```ruby
621
+ ENV["MYTOOL_HOST"] = "127.0.0.1"
622
+ ENV["MYTOOL_PORT"] = "7727"
623
+ ```
624
+
625
+ You can inform configuration about common prefix using `env_prefix`:
605
626
 
606
627
  ```ruby
607
- ENV['MYTOOL_HOST']=localhost
628
+ config.env_prefix = "mytool"
629
+ ```
630
+
631
+ Then set configuration key name to environment variable name:
632
+
633
+ ```ruby
634
+ config.set_from_env(:host)
635
+ config.set_from_env(:port)
636
+ ```
637
+
638
+ And retrieve the value:
639
+
640
+ ```ruby
641
+ config.fetch(:host)
642
+ # => "127.0.0.1"
643
+ config.fetch(:port)
644
+ # => "7727"
645
+ ```
646
+
647
+ ### 2.20 env_separator=
648
+
649
+ By default, the `_` character is used to separate parts in the environment variable name and it can be changed using the `env_separator=` like so:
650
+
651
+ ```ruby
652
+ config.env_separator = "___"
653
+ ```
654
+
655
+ Given the following environment variable:
656
+
657
+ ```ruby
658
+ ENV["SERVER__PORT"] = "123"
659
+ ```
660
+
661
+ Then we can make configuration aware of the above variable name in one of these ways:
662
+
663
+ ```ruby
664
+ config.set_from_env(:server, :port)
665
+ config.set_from_env("server.port")
666
+ ````
667
+
668
+ And retrieve the value:
669
+
670
+ ```ruby
671
+ config.fetch(:server, :port)
672
+ # => "123"
673
+ ```
674
+
675
+ ### 2.21 autoload_env
676
+
677
+ The `autoload_env` method allows you to automatically read environment variables. In most cases you would combine it with [env_prefix=](#219-env_prefix) to only read a subset of variables. When using `autoload_env`, anytime the `fetch` is called a corresponding environment variable will be checked.
678
+
679
+ For example, given an environment variable `MYTOOL_HOST` set to `localhost`:
680
+
681
+ ```ruby
682
+ ENV["MYTOOL_HOST"]=localhost
608
683
  ```
609
684
 
610
685
  And loading environment variables with a prefix of `MYTOOL`:
611
686
 
612
687
  ```ruby
613
- config.env_prefix = 'mytool'
688
+ config.env_prefix = "mytool"
614
689
  config.autoload_env
615
690
  ```
616
691
 
@@ -618,7 +693,84 @@ You can retrieve value with:
618
693
 
619
694
  ```ruby
620
695
  config.fetch(:host)
621
- # => 'localhost'
696
+ # => "localhost"
697
+ ```
698
+
699
+ ### 2.22 register_marshaller
700
+
701
+ There are number of built-in marshallers that handle the process of serializing internal configuration from and back into a desired format, for example, a `JSON` string.
702
+
703
+ Currently supported formats out-of-the-box are: `YAML`, `JSON`, `TOML`, `INI` & `HCL`.
704
+
705
+ To create your own marshaller use the `TTY::Config::Marshaller` interface. You need to provide the implementation for the following marshalling methods:
706
+
707
+ * `marshal`
708
+ * `unmarshal`
709
+
710
+ In addition, you will need to specify the extension types this marshaller will handle using the `extension` method. The method accepts a list of names preceded by a dot:
711
+
712
+ ```ruby
713
+ extension ".ext1", ".ext2", ".ext3"
714
+ ```
715
+
716
+ Optionally, you can provide a dependency or dependencies that will be lazy loaded if the extension is used. For this use the `dependency` method.
717
+
718
+ You can either specify dependencies as a list of names:
719
+
720
+ ```ruby
721
+ dependency "toml"
722
+ dependency "toml", "tomlrb"
723
+ ```
724
+
725
+ Or provide dependencies in a block:
726
+
727
+ ```ruby
728
+ dependency do
729
+ require "toml"
730
+ require "tomlrb"
731
+ end
732
+ ```
733
+
734
+ Putting it all together, you can create your own marshaller like so:
735
+
736
+ ```ruby
737
+ class MyCustomMarshaller
738
+ include TTY::Config::Marshaller
739
+
740
+ dependency "my_dep"
741
+
742
+ extension ".ext1", ".ext2"
743
+
744
+ def marshal(object)
745
+ MyDep.dump(object)
746
+ end
747
+
748
+ def unmarshal(content)
749
+ MyDep.parse(content)
750
+ end
751
+ end
752
+ ```
753
+
754
+ And then let the configuration know about your marshaller by calling the `register_marshaller`:
755
+
756
+ ```ruby
757
+ config.register_marshaller(:my_custom, MyCustomMarshaller)
758
+ ```
759
+
760
+ Bear in mind that you can also override the built-in implementation of a marshaller. For example, if you find a better performing Ruby gem for TOML parsing, register your custom marshaller under the `:toml` name like so:
761
+
762
+ ```ruby
763
+ config.register_marshaller(:toml, MyTOMLMarshaller)
764
+ ```
765
+
766
+ ### 2.23 unregister_marshaller
767
+
768
+ By default, the **TTY::Config** is ready to recognize various extensions. See [2.16 read](#216-read) section for more details. But, you're free to remove the default marshallers from the internal registry with `unregister_marshaller` method.
769
+
770
+ For example, to remove all the built-in marshallers do:
771
+
772
+ ```ruby
773
+ config.unregister_marshaller :yaml, :json, :toml, :ini, :hcl
622
774
  ```
623
775
 
624
776
  ## 3. Examples
@@ -628,22 +780,22 @@ config.fetch(:host)
628
780
  *TTY::Config* fully supports working with environment variables. For example, there are couple of environment variables that your configuration is interested in, which normally would be set in terminal but for the sake of this example we assign them:
629
781
 
630
782
  ```ruby
631
- ENV['MYTOOL_HOST'] = '192.168.1.17'
632
- ENV['MYTOOL_PORT'] = '7727'
783
+ ENV["MYTOOL_HOST"] = "192.168.1.17"
784
+ ENV["MYTOOL_PORT"] = "7727"
633
785
  ```
634
786
 
635
- Then in order to make your configuration aware of the above, you would use [env_prefix=](#212-env_prefix) and [set_from_env](#23-set_from_env):
787
+ Then in order to make your configuration aware of the above, you would use [env_prefix=](#219-env_prefix) and [set_from_env](#23-set_from_env):
636
788
 
637
789
  ```ruby
638
- config.env_prefix = 'mytool'
790
+ config.env_prefix = "mytool"
639
791
  config.set_from_env(:host)
640
792
  config.set_from_env(:port)
641
793
  ```
642
794
 
643
- or automatically load all prefixed environment variables with [autoload_env](#220-autoload-env):
795
+ Or automatically load all prefixed environment variables with [autoload_env](#221-autoload_env):
644
796
 
645
797
  ```ruby
646
- config.env_prefix = 'mytool'
798
+ config.env_prefix = "mytool"
647
799
  config.autoload_env
648
800
  ```
649
801
 
@@ -651,9 +803,9 @@ And then retrieve values with [fetch](#24-fetch):
651
803
 
652
804
  ```ruby
653
805
  config.fetch(:host)
654
- #=> '192.168.1.17'
806
+ #=> "192.168.1.17"
655
807
  config.fetch(:port)
656
- # => '7727'
808
+ # => "7727"
657
809
  ```
658
810
 
659
811
  ### 3.2 Working with optparse
@@ -665,7 +817,7 @@ Let's assume you want to create a command line tool that among many options acce
665
817
  First, you need to parse the flags and store results away in options hash:
666
818
 
667
819
  ```ruby
668
- require 'optparse'
820
+ require "optparse"
669
821
 
670
822
  options = {}
671
823
 
@@ -686,7 +838,7 @@ end
686
838
  option_parser.parse!
687
839
  ```
688
840
 
689
- Then, you craete a configuration instance:
841
+ Then, you create a configuration instance:
690
842
 
691
843
  ```ruby
692
844
  config = TTY::Config.new
@@ -695,7 +847,7 @@ config = TTY::Config.new
695
847
  And setup config filename:
696
848
 
697
849
  ```ruby
698
- config_filename = options[:config_file_path] || 'config.yml'
850
+ config_filename = options[:config_file_path] || "config.yml"
699
851
  ```
700
852
 
701
853
  As well as add configuration file locations to search in:
@@ -709,7 +861,7 @@ Once config is initialized, you can read the configuration from a config file:
709
861
 
710
862
  ```ruby
711
863
  begin
712
- config.read(config_filename) # by default the 'config.yml' is read
864
+ config.read(config_filename) # by default the "config.yml" is read
713
865
  rescue TTY::Config::ReadError => read_error
714
866
  STDERR.puts "\nNo configuration file found:"
715
867
  STDERR.puts read_error
@@ -739,7 +891,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
739
891
 
740
892
  ## Contributing
741
893
 
742
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tty-config. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
894
+ Bug reports and pull requests are welcome on GitHub at https://github.com/piotrmurach/tty-config. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
743
895
 
744
896
  ## License
745
897
 
@@ -747,7 +899,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
747
899
 
748
900
  ## Code of Conduct
749
901
 
750
- Everyone interacting in the Tty::Config project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/tty-config/blob/master/CODE_OF_CONDUCT.md).
902
+ Everyone interacting in the TTY::Config project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/tty-config/blob/master/CODE_OF_CONDUCT.md).
751
903
 
752
904
  ## Copyright
753
905