tty-config 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b7d2891f50d6ec7735ddf4c68e811d2b9726d5f1
4
- data.tar.gz: a4c6a91a265b84c849aa7be416eb0062d9253ff7
2
+ SHA256:
3
+ metadata.gz: '082839e3aab5e29ce83325a7c96c2c6ce636fde381f4dc4c39cf66309bdb10b7'
4
+ data.tar.gz: c0be15cc9d7e0f211fefe9a9e6317f3076b26bfe2bff4a13eb4c0b2ce68a6174
5
5
  SHA512:
6
- metadata.gz: 2f735b5c715194df5873b78bee19062392effc855f712659ab3fc07cd6c87737eedcc79fbce15362ecbe473df226090a3d76c00c1438f0f522c265f742141c50
7
- data.tar.gz: 282c1817e5c73f18bf1be3c0b2c32d41d142a22a894ab5c528901bdfda0a573cbda9cfe74961b92dfe63ee91e89d89f2e06649cd4c208c7e85db47e226dd2ce5
6
+ metadata.gz: fb63f5a33972507ebdc97c4986117b93085314b2a66bd0ed455c2c7bda03ff520f69eee72b72350a868e9370ab287980e34ef60a008c0dbe4917cd6cce8a26bd
7
+ data.tar.gz: 88b31c1a9cc6023c9057b9ea8c7970e791d3de9c64118c8ddc0b753a48c643f5e62bd364c2089feccc40ca1977d821a5c0ab7ca6425ea7e5d69dcf04d03d43cd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.5.0] - 2021-05-23
4
+
5
+ ### Added
6
+ * Add :create option to the #write method to control creating any missing directories
7
+ * Add :path option to the #write method to specify a custom file path to write to
8
+ * Add ability to specify a default value for a missing key in the #delete method
9
+ * Add #env_separator for setting a string to separate parts in environment variable name
10
+
11
+ ### Changed
12
+ * Change #delete to allow removing any subkey of a deeply nested key
13
+ * Change #remove to require from keyword
14
+ * Change #set_if_empty to use nested key fetching to check for value presence
15
+
16
+ ### Fixed
17
+ * Fix Marshaller interface to copy extension names to a subclass
18
+
19
+ ## [v0.4.0] - 2020-01-25
20
+
21
+ ### Added
22
+ * Add DependencyLoader for a generic interface for loading marshalling dependencies
23
+ * Add Marshaller as a generic interface for building marshalling plugins
24
+ * Add MarshallerRegistry for storing all marshallers
25
+ * Add Marshallers to allow configuration of marshallers via #register_marshaller
26
+ & #unregister_marshaller
27
+
28
+ ### Changed
29
+ * Change #initialize to accept hash as settings
30
+ * Change #marshal & #unmarshal to use marshalling plugins
31
+ * Change gemspec to add metadata and remove test artefacts
32
+
33
+ ## [v0.3.2] - 2019-06-18
34
+
35
+ ### Changed
36
+ * Change to relax development dependency versions
37
+
38
+ ### Fixed
39
+ * Fix #read to allow reading empty files
40
+
41
+ ## [v0.3.1] - 2019-01-24
42
+
43
+ ### Fixed
44
+ * Fix references to File class constant by Taylor Thurlow (@taylorthurlow)
45
+
46
+ ## [v0.3.0] - 2018-10-20
47
+
48
+ ### Added
49
+ * Add #set_from_env for binding keys to environment variables
50
+ * Add #env_prefix for setting environment variables prefix
51
+ * Add #autoload_env for loading all environment variables
52
+ * Add #generate for generating file content from the settings
53
+ * Add #alias_setting for aliasing settings
54
+ * Add ability to read & write INI file types
55
+
56
+ ### Changed
57
+ * Change #fetch to read environment variables before defaults
58
+ * Change #fetch to handle aliased settings
59
+ * Change to remove stdout output when dependency cannot be loaded
60
+ * Change to allow for config files without any extension
61
+
3
62
  ## [v0.2.0] - 2018-05-07
4
63
 
5
64
  ### Added
@@ -12,5 +71,10 @@
12
71
 
13
72
  * Initial implementation and release
14
73
 
74
+ [v0.5.0]: https://github.com/piotrmurach/tty-config/compare/v0.4.0...v0.5.0
75
+ [v0.4.0]: https://github.com/piotrmurach/tty-config/compare/v0.3.2...v0.4.0
76
+ [v0.3.2]: https://github.com/piotrmurach/tty-config/compare/v0.3.1...v0.3.2
77
+ [v0.3.1]: https://github.com/piotrmurach/tty-config/compare/v0.3.0...v0.3.1
78
+ [v0.3.0]: https://github.com/piotrmurach/tty-config/compare/v0.2.0...v0.3.0
15
79
  [v0.2.0]: https://github.com/piotrmurach/tty-config/compare/v0.1.0...v0.2.0
16
- [v0.1.0]: https://github.com/piotrmurach/tty-config/compare/v0.1.0
80
+ [v0.1.0]: https://github.com/piotrmurach/tty-config/compare/19cd277...v0.1.0
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Piotr Murach
3
+ Copyright (c) 2018 Piotr Murach (piotrmurach.com)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,36 +1,46 @@
1
+ <div align="center">
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
+ </div>
4
+
1
5
  # TTY::Config [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
2
6
 
3
7
  [![Gem Version](https://badge.fury.io/rb/tty-config.svg)][gem]
4
- [![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]
5
9
  [![Build status](https://ci.appveyor.com/api/projects/status/2383i0dn3hlw9cnn?svg=true)][appveyor]
6
10
  [![Maintainability](https://api.codeclimate.com/v1/badges/dfac05073e1549e9dbb6/maintainability)][codeclimate]
7
11
  [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-config/badge.svg)][coverage]
8
- [![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]
9
13
 
10
14
  [gitter]: https://gitter.im/piotrmurach/tty
11
- [gem]: http://badge.fury.io/rb/tty-config
12
- [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
13
17
  [appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-config
14
18
  [codeclimate]: https://codeclimate.com/github/piotrmurach/tty-config/maintainability
15
19
  [coverage]: https://coveralls.io/github/piotrmurach/tty-config
16
- [inchpages]: http://inch-ci.org/github/piotrmurach/tty-config
20
+ [inchpages]: https://inch-ci.org/github/piotrmurach/tty-config
17
21
 
18
- > 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.
19
23
 
20
- **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.
21
25
 
22
26
  ## Features
23
27
 
24
- * Read & write configurations in YAML, JSON, TOML formats
25
- * Simple interface for setting and fetching values for deeply nested keys
26
- * Merging of configuration options from other hashes
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)
27
37
 
28
38
  ## Installation
29
39
 
30
40
  Add this line to your application's Gemfile:
31
41
 
32
42
  ```ruby
33
- gem 'tty-config'
43
+ gem "tty-config"
34
44
  ```
35
45
 
36
46
  And then execute:
@@ -48,20 +58,30 @@ Or install it yourself as:
48
58
  * [2. Interface](#2-interface)
49
59
  * [2.1 set](#21-set)
50
60
  * [2.2 set_if_empty](#22-set_if_empty)
51
- * [2.3 fetch](#23-fetch)
52
- * [2.4 merge](#24-merge)
53
- * [2.5 coerce](#25-coerce)
54
- * [2.6 append](#26-append)
55
- * [2.7 remove](#27-remove)
56
- * [2.8 delete](#28-delete)
57
- * [2.9 validate](#29-validate)
58
- * [2.10 filename=](#210-filename)
59
- * [2.11 extname=](#211-extname)
60
- * [2.12 append_path](#212-append_path)
61
- * [2.13 prepend_path](#213-prepend_path)
62
- * [2.14 read](#214-read)
63
- * [2.15 write](#215-write)
64
- * [2.16 persisted?](#216-persisted)
61
+ * [2.3 set_from_env](#23-set_from_env)
62
+ * [2.4 fetch](#24-fetch)
63
+ * [2.5 merge](#25-merge)
64
+ * [2.6 coerce](#26-coerce)
65
+ * [2.7 append](#27-append)
66
+ * [2.8 remove](#28-remove)
67
+ * [2.9 delete](#29-delete)
68
+ * [2.10 alias_setting](#210-alias_setting)
69
+ * [2.11 validate](#211-validate)
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)
82
+ * [3. Examples](#3-examples)
83
+ * [3.1 Working with env vars](#31-working-with-env-vars)
84
+ * [3.2 Working with optparse](#32-working-with-optparse)
65
85
 
66
86
  ## 1. Usage
67
87
 
@@ -69,30 +89,30 @@ Initialize the configuration and provide the name:
69
89
 
70
90
  ```ruby
71
91
  config = TTY::Config.new
72
- config.filename = 'investments'
92
+ config.filename = "investments"
73
93
  ```
74
94
 
75
- then configure values for different nested keys with `set` and `append`:
95
+ Then configure values for different nested keys with `set` and `append`:
76
96
 
77
97
  ```ruby
78
- config.set(:settings, :base, value: 'USD')
98
+ config.set(:settings, :base, value: "USD")
79
99
  config.set(:settings, :color, value: true)
80
- config.set(:coins, value: ['BTC'])
100
+ config.set(:coins, value: ["BTC"])
81
101
 
82
- config.append('ETH', 'TRX', 'DASH', to: :coins)
102
+ config.append("ETH", "TRX", "DASH", to: :coins)
83
103
  ```
84
104
 
85
- get any value by using `fetch`:
105
+ You can get any value by using `fetch`:
86
106
 
87
107
  ```ruby
88
108
  config.fetch(:settings, :base)
89
- # => 'USD'
109
+ # => "USD"
90
110
 
91
111
  config.fetch(:coins)
92
- # => ['BTC', 'ETH', 'TRX', 'DASH']
112
+ # => ["BTC", "ETH", "TRX", "DASH"]
93
113
  ```
94
114
 
95
- and `write` configration out to `investments.yml`:
115
+ And call `write` to persist the configuration to `investments.yml` file:
96
116
 
97
117
  ```ruby
98
118
  config.write
@@ -108,14 +128,14 @@ config.write
108
128
  # - DASH
109
129
  ```
110
130
 
111
- 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:
112
132
 
113
133
  ```ruby
114
134
  config.append_path Dir.pwd
115
135
  config.append_path Dir.home
116
136
  ```
117
137
 
118
- Finally, read in configuration back again:
138
+ Finally, call `read` to convert configuration file back into an object again:
119
139
 
120
140
  ```ruby
121
141
  config.read
@@ -131,8 +151,8 @@ class App
131
151
 
132
152
  def initialize
133
153
  @config = TTY::Config.new
134
- @config.filename = 'investments'
135
- @config.extname = '.toml'
154
+ @config.filename = "investments"
155
+ @config.extname = ".toml"
136
156
  @config.append_path Dir.pwd
137
157
  @config.append_path Dir.home
138
158
  end
@@ -150,22 +170,22 @@ end
150
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:
151
171
 
152
172
  ```ruby
153
- config.set(:base, value: 'USD')
154
- config.set(:base) { 'USD' }
173
+ config.set(:base, value: "USD")
174
+ config.set(:base) { "USD" }
155
175
  ```
156
176
 
157
- 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.
158
178
 
159
179
  You can also specify deeply nested configuration settings by passing sequence of keys:
160
180
 
161
181
  ```ruby
162
- config.set :settings, :base, value: 'USD'
182
+ config.set(:settings, :base, value: "USD")
163
183
  ```
164
184
 
165
- is equivalent to:
185
+ Which is equivalent to:
166
186
 
167
187
  ```ruby
168
- config.set 'settings.base', value: 'USD'
188
+ config.set("settings.base", value: "USD")
169
189
  ```
170
190
 
171
191
  Internally all configuration settings are stored as string keys for ease of working with configuration files and command line application's inputs.
@@ -175,22 +195,76 @@ Internally all configuration settings are stored as string keys for ease of work
175
195
  To set a configuration setting only if it hasn't been set before use `set_if_empty`:
176
196
 
177
197
  ```ruby
178
- config.set_if_empty :base, value: 'USD'
198
+ config.set_if_empty(:base, value: "USD")
179
199
  ```
180
200
 
181
201
  Similar to `set` it allows you to specify arbitrary sequence of keys followed by a key value or block:
182
202
 
183
203
  ```ruby
184
- config.set_if_empty :settings, :base, value: 'USD'
204
+ config.set_if_empty(:settings, :base, value: "USD")
205
+ ```
206
+
207
+ ### 2.3 set_from_env
208
+
209
+ To read configuration options from environment variables use `set_from_env`. At minimum it requires a single argument which will match the name of `ENV` variable. The name of this parameter is case insensitive.
210
+
211
+ Given the following environment variables:
212
+
213
+ ```ruby
214
+ ENV["HOST"] = "192.168.1.17"
215
+ ENV["PORT"] = "7727"
216
+ ```
217
+
218
+ You can make the config aware of the above env variables:
219
+
220
+ ```ruby
221
+ config.set_from_env(:host)
222
+ config.set_from_env(:port)
223
+ ```
224
+
225
+ Then you can retrieve values like any other configuration option:
226
+
227
+ ```ruby
228
+ config.fetch(:host)
229
+ # => "192.168.1.17"
230
+ config.fetch(:port)
231
+ # => "7727"
232
+ ```
233
+
234
+ If you want the configuration key name to be different from `ENV` variable name use a block:
235
+
236
+ ```ruby
237
+ config.set_from_env(:host) { "HOSTNAME" }
238
+ config.set_from_env(:host) { :hostname }
185
239
  ```
186
240
 
187
- ### 2.3 fetch
241
+ You can also configure settings for deeply nested keys:
242
+
243
+ ```ruby
244
+ config.set_from_env(:settings, :base) { "CURRENCY" }
245
+ config.set_from_env(:settings, :base) { :currency }
246
+ config.set_from_env("settings.base") { "CURRENCY" }
247
+ config.set_from_env("settings.base") { :currency }
248
+ ```
249
+
250
+ And assuming `ENV["CURRENCY"]=USD`:
251
+
252
+ ```ruby
253
+ config.fetch(:settings, :base)
254
+ # => USD
255
+ ```
256
+
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).
258
+
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.
260
+
261
+ ### 2.4 fetch
188
262
 
189
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:
190
264
 
191
265
  ```ruby
192
- config.fetch(:base, default: 'USD')
193
- config.fetch(:base) { 'USD' }
266
+ config.fetch(:base, default: "USD")
267
+ config.fetch(:base) { "USD" }
194
268
  ```
195
269
 
196
270
  Similar to `set` operation, `fetch` allows you to retrieve deeply nested values:
@@ -199,22 +273,22 @@ Similar to `set` operation, `fetch` allows you to retrieve deeply nested values:
199
273
  config.fetch(:settings, :base) # => USD
200
274
  ```
201
275
 
202
- is equivalent to:
276
+ Which is equivalent to:
203
277
 
204
278
  ```ruby
205
- config.fetch('settings.base')
279
+ config.fetch("settings.base")
206
280
  ```
207
281
 
208
282
  `fetch` has indifferent access so you can mix string and symbol keys, all the following examples retrieve the value:
209
283
 
210
284
  ```ruby
211
285
  config.fetch(:settings, :base)
212
- config.fetch('settings', 'base')
213
- config.fetch(:settings', 'base')
214
- config.fetch('settings', :base)
286
+ config.fetch("settings", "base")
287
+ config.fetch(:settings, "base")
288
+ config.fetch("settings", :base)
215
289
  ```
216
290
 
217
- ### 2.4 merge
291
+ ### 2.5 merge
218
292
 
219
293
  To merge in other configuration settings as hash use `merge`:
220
294
 
@@ -222,7 +296,7 @@ To merge in other configuration settings as hash use `merge`:
222
296
  config.set(:a, :b, value: 1)
223
297
  config.set(:a, :c, value: 2)
224
298
 
225
- config.merge({'a' => {'c' => 3, 'd' => 4}})
299
+ config.merge({"a" => {"c" => 3, "d" => 4}})
226
300
 
227
301
  config.fetch(:a, :c) # => 3
228
302
  config.fetch(:a, :d) # => 4
@@ -230,7 +304,7 @@ config.fetch(:a, :d) # => 4
230
304
 
231
305
  Internally all configuration settings are stored as string keys for ease of working with file values and command line applications inputs.
232
306
 
233
- ### 2.5 coerce
307
+ ### 2.6 coerce
234
308
 
235
309
  You can initialize configuration based on a hash, with all the keys converted to symbols:
236
310
 
@@ -242,7 +316,7 @@ config.to_h
242
316
  # {settings: {base: "USD", exchange: "CCCAGG"}}
243
317
  ```
244
318
 
245
- ### 2.6 append
319
+ ### 2.7 append
246
320
 
247
321
  To append arbitrary number of values to a value under a given key use `append`:
248
322
 
@@ -264,7 +338,7 @@ config.append("EUR", "GBP", to: [:settings, :bases])
264
338
  # {settings: {bases: ["USD", "EUR", "GBP"]}}
265
339
  ```
266
340
 
267
- ### 2.7 remove
341
+ ### 2.8 remove
268
342
 
269
343
  Use `remove` to remove a set of values from a key.
270
344
 
@@ -286,12 +360,12 @@ config.remove("TRX", "DASH", from: [:holdings, :coins])
286
360
  # ["BTC", "ETH"]
287
361
  ```
288
362
 
289
- ### 2.8 delete
363
+ ### 2.9 delete
290
364
 
291
365
  To completely delete a value and corresponding key use `delete`:
292
366
 
293
367
  ```ruby
294
- config.set(:base, "USD")
368
+ config.set(:base, value: "USD")
295
369
  config.delete(:base)
296
370
  # =>
297
371
  # "USD"
@@ -300,13 +374,66 @@ config.delete(:base)
300
374
  You can also delete deeply nested keys and their values:
301
375
 
302
376
  ```ruby
303
- config.set(:settings, :base, "USD")
377
+ config.set(:settings, :base, value: "USD")
304
378
  config.delete(:settings, :base)
305
379
  # =>
306
380
  # "USD"
307
381
  ```
308
382
 
309
- ### 2.9 validate
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
+
391
+ ### 2.10 alias_setting
392
+
393
+ In order to alias a configuration setting to another name use `alias_setting`.
394
+
395
+ For example, given an already existing setting:
396
+
397
+ ```ruby
398
+ config.set(:base, value: "baz")
399
+ ```
400
+
401
+ You can alias it to another name:
402
+
403
+ ```ruby
404
+ config.alias_setting(:base, to: :currency)
405
+ ```
406
+
407
+ And then access like any other configuration setting:
408
+
409
+ ```ruby
410
+ config.fetch(:currency)
411
+ # => "USD"
412
+ ```
413
+
414
+ Deep nested configuration options are also supported:
415
+
416
+ ```ruby
417
+ config.set(:settings, :base, value: "USD")
418
+ ```
419
+
420
+ And then can be aliased like so:
421
+
422
+ ```ruby
423
+ config.alias_setting(:settings, :base, to: [:settings, :currency])
424
+ config.alias_setting("settings.base", to [:settings, :currency])
425
+ ```
426
+
427
+ You can then access the deep nested settings:
428
+
429
+ ```ruby
430
+ config.fetch(:settings, :currency)
431
+ # => "USD"
432
+ config.fetch("settings.currency")
433
+ # => "USD"
434
+ ```
435
+
436
+ ### 2.11 validate
310
437
 
311
438
  To ensure consistency of the data, you can validate values being set at arbitrarily deep keys using `validate` method, that takes an arbitrarily nested key as its argument and a validation block.
312
439
 
@@ -320,40 +447,40 @@ end
320
447
 
321
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.
322
449
 
323
- When setting value all the validaitons will be run:
450
+ When setting value all the validations will be run:
324
451
 
325
452
  ```ruby
326
- config.set(:settings, :base, value: 'PL')
327
- # 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."
328
455
  ```
329
456
 
330
- 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:
331
458
 
332
459
  ```ruby
333
- config.set(:settings, :base) { 'PL' }
460
+ config.set(:settings, :base) { "PL" }
334
461
  config.fetch(:settings, :base)
335
- # raises TTY::Config::ValidationError, 'Currency code needs to be 3 chars long.'
462
+ # raises TTY::Config::ValidationError, "Currency code needs to be 3 chars long."
336
463
  ```
337
464
 
338
- ### 2.10 filename=
465
+ ### 2.12 filename=
339
466
 
340
467
  By default, **TTY::Config** searches for `config` named configuration file. To change this use `filename=` method without the extension name:
341
468
 
342
469
  ```ruby
343
- config.filename = 'investments'
470
+ config.filename = "investments"
344
471
  ```
345
472
 
346
- 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`.
347
474
 
348
- ### 2.11 extname=
475
+ ### 2.13 extname=
349
476
 
350
- 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=`:
351
478
 
352
479
  ```ruby
353
- config.extname = '.toml'
480
+ config.extname = ".toml"
354
481
  ```
355
482
 
356
- ### 2.12 append_path
483
+ ### 2.14 append_path
357
484
 
358
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.
359
486
 
@@ -365,9 +492,9 @@ config.append_path(Dir.home) # look in user's home directory
365
492
  config.append_path(Dir.pwd) # look in current working directory
366
493
  ```
367
494
 
368
- 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.
369
496
 
370
- ### 2.13 prepend_path
497
+ ### 2.15 prepend_path
371
498
 
372
499
  The `prepend_path` allows you to add configuration search paths that should be searched first.
373
500
 
@@ -376,15 +503,26 @@ config.append_path(Dir.pwd) # look in current working directory second
376
503
  config.prepend_path(Dir.home) # look in user's home directory first
377
504
  ```
378
505
 
379
- ### 2.14 read
506
+ ### 2.16 read
507
+
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.
509
+
510
+ Currently the supported file formats are:
380
511
 
381
- There are two ways for reading configuration files and both use the `read` method.
512
+ * `yaml` for `.yaml`, `.yml` extensions
513
+ * `json` for `.json` extension
514
+ * `toml` for `.toml` extension
515
+ * `ini` for `.ini`, `.cnf`, `.conf`, `.cfg`, `.cf extensions`
516
+ * `hcl` for `.hcl` extensions
517
+ * `jprops` for `.properties`, `.props`, `.prop` extensions
382
518
 
383
- First one, searches through provided locations to find configuration file and read it. Therefore, you need to specify at least one search path that contains the configuration file.
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.
520
+
521
+ For example, to find file called investments in the current directory do:
384
522
 
385
523
  ```ruby
386
524
  config.append_path(Dir.pwd) # look in current working directory
387
- config.filename = 'investments' # file to search for
525
+ config.filename = "investments" # file to search for
388
526
  ```
389
527
 
390
528
  Find and read the configuration file:
@@ -393,48 +531,356 @@ Find and read the configuration file:
393
531
  config.read
394
532
  ```
395
533
 
396
- However, you can also specify directly the file to read without setting up any search paths or filenames:
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`:
397
535
 
398
536
  ```ruby
399
- config.read('./investments.toml')
537
+ config.read("./investments.toml")
400
538
  ```
401
539
 
402
- ### 2.15 write
540
+ In cases where you wish to specify a custom file extension, you will need to also specify the file format to use.
403
541
 
404
- 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:
542
+ For example, if you have a configuration file formatted using `YAML` notation with extension called `.config`, to read it do:
405
543
 
406
544
  ```ruby
407
- config.filename = 'investments'
408
- config.extname = '.toml'
545
+ config.read("investments.config", format: :yaml)
409
546
  ```
410
547
 
411
- To write current configuration to a file, you can either specified direct location path and filename:
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:
412
551
 
413
552
  ```ruby
414
- config.write('./investments.toml')
553
+ config.filename = "investments"
554
+ config.extname = ".toml"
415
555
  ```
416
556
 
417
- Or, specify location paths to be searched for already existing configuration to overwrite:
557
+ Now, by invoking `write` you will persist the current configuration to `investments.toml` file.
418
558
 
419
559
  ```ruby
420
- config.append_path(Dir.pwd) # search current working directory
560
+ config.write # writes "investments.toml" in the current directory
561
+ ```
421
562
 
422
- config.write
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:
564
+
565
+ ```ruby
566
+ config.write("/custom/path/to/investments.toml")
567
+ # may raise an error if any of the path directories are missing
568
+ ```
569
+
570
+ Alternatively, if the filename doesn't need to change you can specify only a custom path using the `:path` keyword:
571
+
572
+ ```ruby
573
+ config.write(path: "/custom/path/to")
574
+ # may raise an error if any of the path directories are missing
575
+ ```
576
+
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
+
579
+ ```ruby
580
+ config.write("/custom/path/to/investments.toml", create: true)
581
+ config.write(path: "/custom/path/to", create: true)
582
+ ```
583
+
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
598
+ ```
599
+
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`:
603
+
604
+ ```ruby
605
+ config.write(create: true, force: true)
606
+ ```
607
+
608
+ ### 2.18 exist?
609
+
610
+ To check if a configuration file exists within the configured search paths use `exist?` method:
611
+
612
+ ```ruby
613
+ config.exist? # => true
614
+ ```
615
+
616
+ ### 2.19 env_prefix=
617
+
618
+ Given the following variables:
619
+
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`:
626
+
627
+ ```ruby
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
683
+ ```
684
+
685
+ And loading environment variables with a prefix of `MYTOOL`:
686
+
687
+ ```ruby
688
+ config.env_prefix = "mytool"
689
+ config.autoload_env
690
+ ```
691
+
692
+ You can retrieve value with:
693
+
694
+ ```ruby
695
+ config.fetch(:host)
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
774
+ ```
775
+
776
+ ## 3. Examples
777
+
778
+ ### 3.1 Working with env vars
779
+
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:
781
+
782
+ ```ruby
783
+ ENV["MYTOOL_HOST"] = "192.168.1.17"
784
+ ENV["MYTOOL_PORT"] = "7727"
785
+ ```
786
+
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):
788
+
789
+ ```ruby
790
+ config.env_prefix = "mytool"
791
+ config.set_from_env(:host)
792
+ config.set_from_env(:port)
793
+ ```
794
+
795
+ Or automatically load all prefixed environment variables with [autoload_env](#221-autoload_env):
796
+
797
+ ```ruby
798
+ config.env_prefix = "mytool"
799
+ config.autoload_env
800
+ ```
801
+
802
+ And then retrieve values with [fetch](#24-fetch):
803
+
804
+ ```ruby
805
+ config.fetch(:host)
806
+ #=> "192.168.1.17"
807
+ config.fetch(:port)
808
+ # => "7727"
809
+ ```
810
+
811
+ ### 3.2 Working with optparse
812
+
813
+ This is an example of combining `tty-config` with `optparse` stdlib.
814
+
815
+ Let's assume you want to create a command line tool that among many options accepts `--host|-h` and `--port|-p` flags. In addition, these flags will take precedence over the options specified in the configuration file.
816
+
817
+ First, you need to parse the flags and store results away in options hash:
818
+
819
+ ```ruby
820
+ require "optparse"
821
+
822
+ options = {}
823
+
824
+ option_parser = OptionParser.new do |opts|
825
+ opts.on("-h", "--host HOSTNAME_OR_IP", "Hostname or IP Adress") do |h|
826
+ options[:host] = h
827
+ end
828
+ opts.on("-p", "--port PORT", "Port of application", Integer) do |p|
829
+ options[:port] = p
830
+ end
831
+ opts.on("-c", "--config FILE",
832
+ "Read config values from file (defaults: ./config.yml, ~/.config.yml") do |c|
833
+ options[:config_file_path] = c
834
+ end
835
+ ...
836
+ end
837
+
838
+ option_parser.parse!
839
+ ```
840
+
841
+ Then, you create a configuration instance:
842
+
843
+ ```ruby
844
+ config = TTY::Config.new
423
845
  ```
424
846
 
425
- To create configuration file regardless whether it exists or not, use `:force` flag:
847
+ And setup config filename:
426
848
 
427
849
  ```ruby
428
- config.write(force: true) # overwrite any found config file
429
- config.write('./investments.toml', force: true) # overwrite specific config file
850
+ config_filename = options[:config_file_path] || "config.yml"
430
851
  ```
431
852
 
432
- ### 2.16 persisted?
853
+ As well as add configuration file locations to search in:
433
854
 
434
- To check if a configuration file exists within the configured search paths use `persisted?` method:
855
+ ```ruby
856
+ config.append_path Dir.pwd
857
+ config.append_path Dir.home
858
+ ```
859
+
860
+ Once config is initialized, you can read the configuration from a config file:
435
861
 
436
862
  ```ruby
437
- config.persisted? # => true
863
+ begin
864
+ config.read(config_filename) # by default the "config.yml" is read
865
+ rescue TTY::Config::ReadError => read_error
866
+ STDERR.puts "\nNo configuration file found:"
867
+ STDERR.puts read_error
868
+ end
869
+ ```
870
+
871
+ Then merge options passed as arguments with those stored in a configuration file:
872
+
873
+ ```ruby
874
+ config.merge(options)
875
+ ```
876
+
877
+ Provide optional validation to ensure both host and port are configured:
878
+
879
+ ```ruby
880
+ if !config.fetch(:host) || !config.fetch(:port)
881
+ STDERR.puts "Host and port have to be specified (call with --help for help)."
882
+ exit 1
883
+ end
438
884
  ```
439
885
 
440
886
  ## Development
@@ -445,7 +891,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
445
891
 
446
892
  ## Contributing
447
893
 
448
- 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.
449
895
 
450
896
  ## License
451
897
 
@@ -453,7 +899,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
453
899
 
454
900
  ## Code of Conduct
455
901
 
456
- 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).
457
903
 
458
904
  ## Copyright
459
905