README.md in qonfig-0.16.0 vs README.md in qonfig-0.17.0
- old
+ new
@@ -23,43 +23,59 @@
## Usage
- [Definition](#definition)
- [Definition and Settings Access](#definition-and-access)
- [access via method](#access-via-method)
- - [index-method](#index-method)
+ - [access via index-method \[\]](#access-via-index-method-)
- [.dig](#dig)
- [.slice](#slice)
- [.slice_value](#slice_value)
- [.subset](#subset)
- [Configuration](#configuration)
+ - [configure via proc](#configure-via-proc)
+ - [configure via settings object (by option name)](#configure-via-settings-object-by-option-name)
+ - [configure via settings object (by setting key)](#configure-via-settings-object-by-setting-key)
+ - [instant configuration via proc](#instant-configuration-via-proc)
+ - [using a hash](#using-a-hash)
+ - [using both hash and proc](#using-both-hash-and-proc-proc-has-higher-priority)
- [Inheritance](#inheritance)
- [Composition](#composition)
- [Hash representation](#hash-representation)
- [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)
- [Instantiation without class definition](#instantiation-without-class-definition) (`Qonfig::DataSet.build(&definitions)`)
- [Interaction](#interaction)
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
- [Config reloading](#config-reloading) (reload config definitions and option values)
- - [Clear options](#clear-options) (set to nil)
+ - [Clear options](#clear-options) (set to `nil`)
- [State freeze](#state-freeze)
- [Settings as Predicates](#settings-as-predicates)
+ - [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
+ - [Run arbitary code with temporary settings](#run-arbitary-code-with-temporary-settings) (`#with(configs = {}, &arbitary_code)`)
- [Validation](#validation)
- - [Introduction](#introdaction)
+ - [Introduction](#introduction)
- [Key search pattern](#key-search-pattern)
- [Proc-based validation](#proc-based-validation)
- [Method-based validation](#method-based-validation)
- [Predefined validations](#predefined-validations)
- [Work with files](#work-with-files)
- - [Load from YAML file](#load-from-yaml-file)
- - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
- - [Load from JSON file](#load-from-json-file)
- - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
- - [Load from ENV](#load-from-env)
- - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
- - [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
- - [Save to JSON file](#save-to-json-file) (`save_to_json`)
- - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
+ - **Setting keys definition**
+ - [Load from YAML file](#load-from-yaml-file)
+ - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
+ - [Load from JSON file](#load-from-json-file)
+ - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
+ - [Load from ENV](#load-from-env)
+ - [Load from \_\_END\_\_](#load-from-__end__) (aka `.load_from_self`)
+ - [Expose \_\_END\_\_](#expose-__end__) (aka `.expose_self`)
+ - **Setting values**
+ - [Default setting values file](#default-setting-values-file)
+ - [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)
+ - [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)
+ - [Load setting values from \_\_END\_\_](#load-setting-values-from-__end__-by-instance)
+ - [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)
+ - **Daily work**
+ - [Save to JSON file](#save-to-json-file) (`#save_to_json`)
+ - [Save to YAML file](#save-to-yaml-file) (`#save_to_yaml`)
- [Plugins](#plugins)
- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
- [Roadmap](#roadmap)
---
@@ -114,11 +130,11 @@
config.settings.vendor_api.host # => 'app.service.com'
config.settings.vendor_api.user # => 'test_user'
config.settings.enable_graphql # => false
```
-#### index-method []
+#### access via index-method []
```ruby
# get option value via index (with indifferent (string / symbol / mixed) access)
config.settings[:project_id] # => nil
config.settings[:vendor_api][:host] # => 'app.service.com'
@@ -198,44 +214,62 @@
setting :enable_middlewares, false
end
config = Config.new
+```
-# configure via proc
+#### configure via proc
+
+```ruby
config.configure do |conf|
conf.enable_middlewares = true
conf.geo_api.provider = :yandex_maps
conf.testing.engine = :mini_test
end
+```
-# configure via settings object (by option name)
+#### configure via settings object (by option name)
+
+```ruby
config.settings.enable_middlewares = false
config.settings.geo_api.provider = :apple_maps
config.settings.testing.engine = :ultra_test
+```
-# configure via settings object (by setting key)
+#### configure via settings object (by setting key)
+
+```ruby
config.settings[:enable_middlewares] = true
config.settings[:geo_api][:provider] = :rambler_maps
config.settings[:testing][:engine] = :mega_test
+```
-# instant configuration via proc
+#### instant configuration via proc
+
+```ruby
config = Config.new do |conf|
conf.enable_middlewares = false
conf.geo_api.provider = :amazon_maps
conf.testing.engine = :crypto_test
end
+```
-# using a hash
+#### using a hash
+
+```ruby
config = Config.new(
testing: { engine: :mini_test, parallel: false },
geo_api: { provider: :rambler_maps },
enable_middlewares: true
)
config.configure(enable_middlewares: false)
+```
-# using both hash and proc (proc has higher priority)
+#### using both hash and proc (proc has higher priority)
+
+```ruby
config = Config.new(enable_middlewares: true) do |conf|
conf.testing.parallel = true
end
config.configure(geo_api: { provider: nil }) do |conf|
@@ -453,22 +487,29 @@
{ 'user' => '0exp', 'pswd' => '123test', 'db' => { 'adapter' => 'pg' } }
# and etc... (all Qonfig-related features)
```
+---
+
### Instantiation without class definition
```ruby
config = Qonfig::DataSet.build do
setting :user, 'D@iVeR'
setting :password, 'test123'
+
+ def custom_method
+ 'custom_result'
+ end
end
config.is_a?(Qonfig::DataSet) # => true
config.settings.user # => 'D@iVeR'
config.settings.password # => 'test123'
+config.custom_method # => 'custom_result'
```
---
## Interaction
@@ -476,10 +517,12 @@
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
- [Config reloading](#config-reloading) (reload config definitions and option values)
- [Clear options](#clear-options) (set to nil)
- [State freeze](#state-freeze)
- [Settings as Predicates](#settings-as-predicates)
+- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
+- [Run arbitary code with temporary settings](#run-arbitary-code-with-temporary-settings)
---
### Iteration over setting keys
@@ -680,10 +723,76 @@
config.settings.database.engine.driver? # => true (true => true)
```
---
+### Setting key existence
+
+- `#key?(*key_path)` / `#option?(*key_path)` / `#setting?(*key_path)`
+ - `*key_path` - an array of symbols and strings that represents a path to the concrete setting key;
+ - (for example, `config.key?(:credentials, :user)` tries to check that `config.settings.credentials.user` is exist);
+ - returns `true` if the concrete key is exist;
+ - returns `false` if the concrete key does not exist;
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :credentials do
+ setting :user, 'D@iVeR'
+ setting :password, 'test123'
+ end
+end
+
+config = Config.new
+
+config.key?('credentials', 'user') # => true
+config.key?('credentials', 'token') # => false (key does not exist)
+
+config.key?('credentials') # => true
+config.key?('que_adapter') # => false (key does not exist)
+
+# aliases
+config.setting?('credentials') # => true
+config.option?(:credentials, :password) # => true
+```
+
+---
+
+### Run arbitary code with temporary settings
+
+- provides a way to run an arbitary code with temporarily specified settings;
+- your arbitary code can temporary change any setting too - all settings will be returned to the original state;
+- (it is convenient to run code samples by this way in tests (with substitued configs));
+- it is fully thread-safe `:)`;
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :queue do
+ setting :adapter, :sidekiq
+ setting :options, {}
+ end
+end
+
+config = Config.new
+
+# run a block of code with temporary queue.adapter setting
+config.with(queue: { adapter: 'que' }) do
+ # your changed settings
+ config.settings.queue.adapter # => 'que'
+
+ # you can temporary change settings by your code too
+ config.settings.queue.options = { concurrency: 10 }
+
+ # ...your another code...
+end
+
+# original settings has not changed :)
+config.settings.queue.adapter # => :sidekiq
+config.settings.queue.options # => {}
+```
+
+---
+
## Validation
- [Introduction](#introduction)
- [Key Search Pattern](#key-search-pattern)
- [Proc-based validation](#proc-based-validation)
@@ -703,46 +812,51 @@
- validation is invoked on any mutation of any setting:
- during dataset instantiation;
- when assigning new values;
- when calling `#reload!`;
- when calling `#clear!`;
+- provides `strict` and `non-strict` behavior (`strict: true` and `strict: false` respectively):
+ - `strict: false` ignores validations for settings with `nil` (allows `nil` value);
+ - `strict: true` does not ignores validations for settings with `nil`;
+ - `strict: false` is used by default;
- provides special [key search pattern](#key-search-pattern) for matching setting key names;
- uses the [key search pattern](#key-search-pattern) for definging what the setting key should be validated;
- you can define your own custom validation logic and validate dataset instance completely;
- validation logic should return **truthy** or **falsy** value;
- supprots two validation techniques (**proc-based** ([doc](#proc-based-validation)) and **dataset-method-based** ([doc](#method-based-validation))):
- - **proc-based** (`setting validation`)
+ - **proc-based** (`setting validation`) ([doc](#proc-based-validation))
```ruby
- validate 'db.user' do |value|
+ validate('db.user', strict: true) do |value|
value.is_a?(String)
end
```
- - **proc-based** (`dataset validation`)
+ - **proc-based** (`dataset validation`) ([doc](#proc-based-validation))
```ruby
- validate do
+ validate(strict: false) do
settings.user == User[1]
end
```
- - **dataset-method-based** (`setting validation`)
+ - **dataset-method-based** (`setting validation`) ([doc](#method-based-validation))
```ruby
- validate 'db.user', by: :check_user
+ validate 'db.user', by: :check_user, strict: true
def check_user(value)
value.is_a?(String)
end
```
- - **dataset-method-based** (`dataset validation`)
+ - **dataset-method-based** (`dataset validation`) ([doc](#method-based-validation))
```ruby
- validate by: :check_config
+ validate by: :check_config, strict: false
def check_config
settings.user == User[1]
end
```
- provides a **set of standard validations** ([doc](#predefined-validations)):
- DSL: `validate 'key.pattern', :predefned_validator`;
- - validators:
+ - supports `strict` behavior;
+ - realized validators:
- `integer`
- `float`
- `numeric`
- `big_decimal`
- `boolean`
@@ -780,15 +894,17 @@
---
### Proc-based validation
- your proc should return truthy value or falsy value;
+- `nil` values are ignored by default;
+- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
- how to validate setting keys:
- define proc with attribute: `validate 'your.setting.path' do |value|; end`
- proc will receive setting value;
- how to validate dataset instance:
- - define proc without setting key pattern: `validate do; end`
+ - define proc without setting key pattern: `validate do; end`;
```ruby
class Config < Qonfig::DataSet
setting :db do
setting :user, 'D@iVeR'
@@ -803,10 +919,11 @@
seting :admin, 'D@iVeR'
end
end
setting :enabled, false
+ setting :token, '1a2a3a', strict: true
# validates:
# - db.password
validate 'db.password' do |value|
value.is_a?(String)
@@ -823,25 +940,35 @@
# validates:
# - dataset instance
validate do # NOTE: no setting key pattern
settings.enabled == false
end
+
+ # do not ignore `nil` (strict: true)
+ validate(:token, strict: true) do
+ value.is_a?(String)
+ end
end
config = Config.new
config.settings.db.password = 123 # => Qonfig::ValidationError (should be a string)
config.settings.service.address = 123 # => Qonfig::ValidationError (should be a string)
config.settings.service.protocol = :http # => Qonfig::ValidationError (should be a string)
config.settings.service.creds.admin = :billikota # => Qonfig::ValidationError (should be a string)
config.settings.enabled = true # => Qonfig::ValidationError (isnt `true`)
+
+config.settings.db.password = nil # ok, nil is ignored (non-strict behavior)
+config.settings.token = nil # => Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of string)
```
---
### Method-based validation
- method should return truthy value or falsy value;
+- `nil` values are ignored by default;
+- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
- how to validate setting keys:
- define validation: `validate 'db.*.user', by: :your_custom_method`;
- define your method with attribute: `def your_custom_method(setting_value); end`
- how to validate config instance
- define validation: `validate by: :your_custom_method`
@@ -860,10 +987,11 @@
setting :rambler, 0
end
end
setting :enabled, true
+ setting :timeout, 12345, strict: true
# validates:
# - services.counts.google
# - services.counts.rambler
# - services.minimals.google
@@ -872,33 +1000,44 @@
# validates:
# - dataset instance
validate by: :check_state # NOTE: no setting key pattern
+ # do not ignore `nil` (strict: true)
+ validate :timeout, strict: true, by: :check_timeout
+
def check_presence(value)
value.is_a?(Numeric) && value > 0
end
def check_state
settings.enabled.is_a?(TrueClass) || settings.enabled.is_a?(FalseClass)
end
+
+ def check_timeout(value)
+ value.is_a?(Numeric)
+ end
end
config = Config.new
config.settings.counts.google = 0 # => Qonfig::ValidationError (< 0)
-config.settings.counts.rambler = nil # => Qonfig::ValidationError (should be a numeric)
config.settings.minimals.google = -1 # => Qonfig::ValidationError (< 0)
config.settings.minimals.rambler = 'no' # => Qonfig::ValidationError (should be a numeric)
-config.settings.enabled = nil # => Qonfig::ValidationError (should be a boolean)
+
+config.settings.counts.rambler = nil # ok, nil is ignored (default non-strict behavior)
+config.settings.enabled = nil # ok, nil is ignored (default non-strict behavior)
+config.settings.timeout = nil # => Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of numeric)
```
---
### Predefined validations
- DSL: `validate 'key.pattern', :predefned_validator`
+- `nil` values are ignored by default;
+- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
- predefined validators:
- `:not_nil`
- `:integer`
- `:float`
- `:numeric`
@@ -946,19 +1085,27 @@
---
## Work with files
-- [Load from YAML file](#load-from-yaml-file)
-- [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
-- [Load from JSON file](#load-from-json-file)
-- [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
-- [Load from ENV](#load-from-env)
-- [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
-- [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
-- [Save to JSON file](#save-to-json-file) (`save_to_json`)
-- [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
+- **Setting keys definition**
+ - [Load from YAML file](#load-from-yaml-file)
+ - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
+ - [Load from JSON file](#load-from-json-file)
+ - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
+ - [Load from ENV](#load-from-env)
+ - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
+ - [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
+- **Setting values**
+ - [Default setting values file](#default-setting-values-file)
+ - [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)
+ - [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)
+ - [Load setting values from \_\_END\_\_](#load-setting-values-from-__end__-by-instance)
+ - [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)
+- **Daily work**
+ - [Save to JSON file](#save-to-json-file) (`save_to_json`)
+ - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
---
### Load from YAML file
@@ -1486,10 +1633,434 @@
password: test123
```
---
+### Default setting values file
+
+- defines a file that should be used for setting values initialization for your config object;
+- `.values_file(file_path, format: :dynamic, strict: false, expose: nil)`
+ - `file_path` - full file path or `:self` (`:self` menas "load setting values from __END__ data");
+ - `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
+ - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
+ - `:strict` - rerquires that file (or __END__-data) should exist (`false` by default);
+ - `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
+- extra keys that does not exist in your config will cause an exception `Qonfig::SettingNotFound` respectively;
+- initial values will be rewritten by values defined in your file;
+
+#### Default behavior
+
+```yaml
+# sidekiq.yml
+
+adapter: sidekiq
+options:
+ processes: 10
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ values_file 'sidekiq.yml', format: :yaml
+
+ setting :adapter, 'que'
+ setting :options do
+ setting :processes, 2
+ setting :threads, 5
+ setting :protected, false
+ end
+end
+
+config = Config.new
+
+config.settings.adapter # => "sidekiq" (from sidekiq.yml)
+config.settings.options.processes # => 10 (from sidekiq.yml)
+config.settings.options.threads # => 5 (original value)
+config.settings.options.protected # => false (original value)
+```
+
+#### Load values from \_\_END\_\_-data
+
+```ruby
+class Config < Qonfig::DataSet
+ values_file :self, format: :yaml
+
+ setting :user
+ setting :password
+ setting :enabled, true
+end
+
+config = Config.new
+
+config.settings.user # => "D@iVeR" (from __END__ data)
+config.settings.password # => "test123" (from __END__ data)
+config.settings.enabled # => true (original value)
+
+__END__
+
+user: 'D@iVeR'
+password: 'test123'
+```
+
+#### Setting values with environment separation
+
+```yaml
+# sidekiq.yml
+
+development:
+ adapter: :in_memory
+ options:
+ threads: 10
+
+production:
+ adapter: :sidekiq
+ options:
+ threads: 150
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ values_file 'sidekiq.yml', format: :yaml, expose: :development
+
+ setting :adapter
+ setting :options do
+ setting :threads
+ end
+end
+
+config = Config.new
+
+config.settings.adapter # => 'in_memory' (development keys subset)
+config.settings.options.threads # => 10 (development keys subset)
+```
+
+#### File does not exist
+
+```ruby
+# strict behavior (default)
+class Config < Qonfig::DataSet
+ values_file 'sidekiq.yml'
+end
+
+config = Config.new # => Qonfig::FileNotFoundError
+
+# non-strict behavior (strict: false)
+class Config < Qonfig::DataSet
+ values_file 'sidekiq.yml', strict: false
+end
+
+config = Config.new # no error
+```
+
+---
+
+### Load setting values from YAML file (by instance)
+
+- prvoides an ability to load predefined setting values from a yaml file;
+- `#load_from_yaml(file_path, strict: true, expose: nil)`
+ - `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
+ - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
+ - `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
+
+#### Default behavior
+
+```yaml
+# config.yml
+
+domain: google.ru
+creds:
+ auth_token: test123
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ seting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token, 'test'
+ end
+end
+
+config = Config.new
+config.settings.domain # => "test.com"
+config.settings.creds.auth_token # => "test"
+
+# load new values
+config.load_from_yaml('config.yml')
+
+config.settings.domain # => "google.ru" (from config.yml)
+config.settings.creds.auth_token # => "test123" (from config.yml)
+```
+
+#### Load from \_\_END\_\_
+
+```ruby
+class Config < Qonfig::DataSet
+ seting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token, 'test'
+ end
+end
+
+config = Config.new
+config.settings.domain # => "test.com"
+config.settings.creds.auth_token # => "test"
+
+# load new values
+config.load_from_yaml(:self)
+config.settings.domain # => "yandex.ru" (from __END__-data)
+config.settings.creds.auth_token # => "CK0sIdA" (from __END__-data)
+
+__END__
+
+domain: yandex.ru
+creds:
+ auth_token: CK0sIdA
+```
+
+#### Setting values with environment separation
+
+```yaml
+# config.yml
+
+development:
+ domain: dev.google.ru
+ creds:
+ auth_token: kekpek
+
+production:
+ domain: google.ru
+ creds:
+ auth_token: Asod1
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token
+ end
+end
+
+config = Config.new
+
+# load new values (expose development settings)
+config.load_from_yaml('config.yml', expose: :development)
+
+config.settings.domain # => "dev.google.ru" (from config.yml)
+config.settings.creds.auth_token # => "kek.pek" (from config.yml)
+```
+
+---
+
+### Load setting values from JSON file (by instance)
+
+- prvoides an ability to load predefined setting values from a json file;
+- `#load_from_yaml(file_path, strict: true, expose: nil)`
+ - `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
+ - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
+ - `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
+
+#### Default behavior
+
+```json
+// config.json
+
+{
+ "domain": "google.ru",
+ "creds": {
+ "auth_token": "test123"
+ }
+}
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ seting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token, 'test'
+ end
+end
+
+config = Config.new
+config.settings.domain # => "test.com"
+config.settings.creds.auth_token # => "test"
+
+# load new values
+config.load_from_json('config.json')
+
+config.settings.domain # => "google.ru" (from config.json)
+config.settings.creds.auth_token # => "test123" (from config.json)
+```
+
+#### Load from \_\_END\_\_
+
+```ruby
+class Config < Qonfig::DataSet
+ seting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token, 'test'
+ end
+end
+
+config = Config.new
+config.settings.domain # => "test.com"
+config.settings.creds.auth_token # => "test"
+
+# load new values
+config.load_from_json(:self)
+config.settings.domain # => "yandex.ru" (from __END__-data)
+config.settings.creds.auth_token # => "CK0sIdA" (from __END__-data)
+
+__END__
+
+{
+ "domain": "yandex.ru",
+ "creds": {
+ "auth_token": "CK0sIdA"
+ }
+}
+```
+
+#### Setting values with environment separation
+
+```json
+// config.json
+
+{
+ "development": {
+ "domain": "dev.google.ru",
+ "creds": {
+ "auth_token": "kekpek"
+ }
+ },
+ "production": {
+ "domain": "google.ru",
+ "creds": {
+ "auth_token": "Asod1"
+ }
+ }
+}
+```
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :domain, 'test.com'
+ setting :creds do
+ setting :auth_token
+ end
+end
+
+config = Config.new
+
+# load new values (from development subset)
+config.load_from_json('config.json', expose: :development)
+
+config.settings.domain # => "dev.google.ru" (from config.json)
+config.settings.creds.auth_token # => "kek.pek" (from config.json)
+```
+---
+
+### Load setting values from \_\_END\_\_ (by instance)
+
+- prvoides an ability to load predefined setting values from `__END__` file section;
+- `#load_from_self(strict: true, expose: nil)`
+ - `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
+ - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
+ - `:strict` - requires that __END__-data should exist (`true` by default);
+ - `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
+
+#### Default behavior
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :account, 'test'
+ setting :options do
+ setting :login, '0exp'
+ setting :password, 'test123'
+ end
+end
+
+config = Config.new
+config.settings.account # => "test" (original value)
+config.settings.options.login # => "0exp" (original value)
+config.settings.options.password # => "test123" (original value)
+
+# load new values
+config.load_from_self(format: :yaml)
+# or config.load_from_self
+
+config.settings.account # => "real" (from __END__-data)
+config.settings.options.login # => "D@iVeR" (from __END__-data)
+config.settings.options.password # => "azaza123" (from __END__-data)
+
+__END__
+
+account: real
+options:
+ login: D@iVeR
+ password: azaza123
+```
+
+#### Setting values with envvironment separation
+
+```ruby
+class Config < Qonfig::DataSet
+ setting :domain, 'test.google.ru'
+ setting :options do
+ setting :login, 'test'
+ setting :password, 'test123'
+ end
+end
+
+config = Config.new
+config.settings.domain # => "test.google.ru" (original value)
+config.settings.options.login # => "test" (original value)
+config.settings.options.password # => "test123" (original value)
+
+# load new values
+config.load_from_self(format: :json, expose: :production)
+# or config.load_from_self(expose: production)
+
+config.settings.domain # => "prod.google.ru" (from __END__-data)
+config.settings.options.login # => "prod" (from __END__-data)
+config.settings.options.password # => "prod123" (from __END__-data)
+
+__END__
+
+{
+ "development": {
+ "domain": "dev.google.ru",
+ "options": {
+ "login": "dev",
+ "password": "dev123"
+ }
+ },
+ "production": {
+ "domain": "prod.google.ru",
+ "options": {
+ "login": "prod",
+ "password": "prod123"
+ }
+ }
+}
+```
+
+---
+
+### Load setting values from file manually (by instance)
+
+- prvoides an ability to load predefined setting values from a file;
+- works in instance-based `#load_from_yaml` / `#load_from_json` / `#load_from_self` manner;
+- signature: `#load_from_file(file_path, format: :dynamic, strict: true, expose: nil)`:
+ - `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
+ - `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
+ - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
+ - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
+ - `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
+- see examples for instance-based `#load_from_yaml` ([doc](#load-setting-values-from-yaml-by-instance)) / `#load_from_json` ([doc](#load-setting-values-from-json-by-instance)) / `#load_from_self` ([doc](#load-setting-values-from-__end__-by-instance));
+
+---
+
### Save to JSON file
- `#save_to_json` - represents config object as a json structure and saves it to a file:
- uses native `::JSON.generate` under the hood;
- writes new file (or rewrites existing file);
@@ -1628,12 +2199,10 @@
port: 12345
enabled: true
dynamic: 10
```
----
-
### Plugins
```ruby
# --- show names of registered plugins ---
Qonfig.plugins # => array of strings
@@ -1650,14 +2219,16 @@
### Plugins: toml
- adds support for `toml` format ([specification](https://github.com/toml-lang/toml));
- depends on `toml-rb` gem ([link](https://github.com/emancu/toml-rb));
-- supports TOML `0.4.0` format (dependency lock);
-- provides `load_from_toml` (works in `load_from_yaml` manner ([doc](#load-from-yaml-file)));
-- provides `save_to_toml` (works in `save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);
-- provides `expose_toml` (works in `expose_yaml` manner ([doc](#expose-yaml)));
+- supports TOML `0.5.0` format (dependency lock);
+- provides `.load_from_toml` (works in `.load_from_yaml` manner ([doc](#load-from-yaml-file)));
+- provides `.expose_toml` (works in `.expose_yaml` manner ([doc](#expose-yaml)));
+- provides `#save_to_toml` (works in `#save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);
+- provides `format: :toml` for `.values_file` ([doc]());
+- provides `#load_from_toml` (work in `#load_from_yaml` manner ([doc](#load-setting-values-from-yaml)));
```ruby
# 1) require external dependency
require 'toml-rb'
@@ -1668,17 +2239,27 @@
```
---
## Roadmap
-- distributed configuration server;
-- support for Rails-like secrets;
+- **Major**:
+ - distributed configuration server;
+ - cli toolchain;
+ - support for Rails-like secrets;
+ - support for persistent data storages (we want to store configs in multiple databases and files);
+ - Rails reload plugin;
+- **Minor**:
+ - custom global (and class-level) validators (with a special Validator Definition DSL);
+ - support for "dot notation" in `#key?`, `#option?`, `#setting?`, `#dig`, `#subset`, `#slice`, `#slice_value`;
+ - "load setting values from a file" (at instance level);
+ - config improts (and exports);
+ - pretty print :)));
## Contributing
- Fork it ( https://github.com/0exp/qonfig/fork )
- Create your feature branch (`git checkout -b feature/my-new-feature`)
-- Commit your changes (`git commit -am 'Add some feature'`)
+- Commit your changes (`git commit -am '[my-new-featre] Add some feature'`)
- Push to the branch (`git push origin feature/my-new-feature`)
- Create new Pull Request
## License