{"id":16443397,"url":"https://github.com/0exp/qonfig","last_synced_at":"2025-04-05T11:08:52.003Z","repository":{"id":45047799,"uuid":"131208672","full_name":"0exp/qonfig","owner":"0exp","description":"Powerful configuration Ruby-framework with a support for many commonly used config formats with a multi-functional API, developer-friendly DSL and object-oriented behavior.","archived":false,"fork":false,"pushed_at":"2024-11-13T22:38:29.000Z","size":1003,"stargazers_count":23,"open_issues_count":27,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-24T16:30:16.939Z","etag":null,"topics":["config","configurable","json-config","multi-config","ruby-config","ruby-configurable","ruby-settings","settings","toml-config","yaml-config"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0exp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-26T20:44:35.000Z","updated_at":"2024-11-13T22:38:34.000Z","dependencies_parsed_at":"2024-12-14T15:29:15.664Z","dependency_job_id":"82979db2-071c-4776-a400-7d37808f4066","html_url":"https://github.com/0exp/qonfig","commit_stats":{"total_commits":760,"total_committers":11,"mean_commits":69.0909090909091,"dds":0.3302631578947368,"last_synced_commit":"efdac5860add1fb9cfddba781726b7e8fdac0518"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fqonfig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fqonfig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fqonfig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fqonfig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0exp","download_url":"https://codeload.github.com/0exp/qonfig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247325693,"owners_count":20920714,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["config","configurable","json-config","multi-config","ruby-config","ruby-configurable","ruby-settings","settings","toml-config","yaml-config"],"created_at":"2024-10-11T09:20:20.515Z","updated_at":"2025-04-05T11:08:51.982Z","avatar_url":"https://github.com/0exp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Qonfig \u0026middot; [![Gem Version](https://badge.fury.io/rb/qonfig.svg)](https://badge.fury.io/rb/qonfig) [![Build](https://github.com/0exp/qonfig/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/0exp/qonfig/actions)\n\nPowerful configuration Ruby-framework with a support for many commonly used config formats with a multi-functional API, developer-friendly DSL and object-oriented behavior.\n\n- Support for: **YAML**, **TOML**, **JSON**, **ENV**, **\\_\\_END\\_\\_**-instructions;\n- Fully thread-safe;\n- Object-oriented behavior (config as an object, inhertance, composition, etc), with an abilities of lazy-instantiation;\n- Pluggable and extendable multi-functional API;\n- Developer-friendly DSL :)\n\n```\n# in the past...:\n\nConfig. Defined as a class. Used as an instance. Support for inheritance and composition.\nLazy instantiation. Thread-safe. Command-style DSL. Validation layer. **Dot-notation**)\nAnd pretty-print :) Support for **YAML**, **TOML**, **JSON**, **\\_\\_END\\_\\_**, **ENV**.\nExtremely simple to define. Extremely simple to use. That's all? **Not** :)\n```\n\n## Installation\n\n```ruby\ngem 'qonfig'\n```\n\n```shell\n$ bundle install\n# --- or ---\n$ gem install 'qonfig'\n```\n\n```ruby\nrequire 'qonfig'\n```\n\n## Usage\n\n- [Definition](#definition)\n  - [Definition and Settings Access](#definition-and-access)\n    - [access via method](#access-via-method)\n    - [access via index-method \\[\\]](#access-via-index-method-)\n    - [.dig](#dig)\n    - [.slice](#slice)\n    - [.slice_value](#slice_value)\n    - [.subset](#subset)\n  - [Configuration](#configuration)\n    - [configure via proc](#configure-via-proc)\n    - [configure via settings object (by option name)](#configure-via-settings-object-by-option-name)\n    - [configure via settings object (by setting key)](#configure-via-settings-object-by-setting-key)\n    - [instant configuration via proc](#instant-configuration-via-proc)\n    - [using a hash](#using-a-hash)\n    - [using both hash and proc](#using-both-hash-and-proc-proc-has-higher-priority)\n  - [Inheritance](#inheritance)\n  - [Composition](#composition)\n  - [Hash representation](#hash-representation)\n    - [Default behaviour (without options)](#default-behavior-without-options)\n    - [With transformations](#with-transformations)\n    - [Dot-style format](#dot-style-format)\n  - [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)\n  - [Instantiation without class definition](#instantiation-without-class-definition) (`Qonfig::DataSet.build(\u0026definitions)`)\n- [Compacted config](#compacted-config)\n  - [Definition and instantiation](#definition-and-instantiation)\n    - [by raw initialization](#by-raw-initialization)\n    - [by existing Qonfig::DataSet class](#by-existing-qonfigdataset-class)\n    - [by existing Qonfig::DataSet instance](#by-existing-qonfigdataset-instance)\n    - [instantiation without class definition](#instantiation-without-class-definition-1)\n    - [validation API](#validation-api-see-full-documentation)\n  - [Setting readers and writers](#setting-readers-and-writers)\n    - [reading](#reading-by-setting-name-and-index-method-with-dot-notation-support-and-indifferent-access)\n    - [writing](#writing-by-setting-name-and-index-method-with-dot-notation-support-and-indifferent-access)\n    - [precitaes](#predicates-see-full-documentation)\n- [Interaction](#interaction)\n  - [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)\n  - [List of config keys](#list-of-config-keys) (`#keys`, `#root_keys`)\n  - [Config reloading](#config-reloading) (reload config definitions and option values)\n  - [Clear options](#clear-options) (set to `nil`)\n  - [Frozen state](#frozen-state) (`.freeze_state!`, `#freeze!`, `#frozen?`)\n  - [Settings as Predicates](#settings-as-predicates)\n  - [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)\n  - [Run arbitrary code with temporary settings](#run-arbitrary-code-with-temporary-settings) (`#with(configs = {}, \u0026arbitrary_code)`)\n- [Import settings / Export settings](#import-settings--export-settings)\n  - [Import config settings](#import-config-settings) (`as instance methods`)\n    - [Import a set of setting keys (simple dot-noated key list)](#import-a-set-of-setting-keys-simple-dot-noated-key-list)\n    - [Import with custom method names (mappings)](#import-with-custom-method-names-mappings)\n    - [Prexify method name](#prexify-method-name)\n    - [Import nested settings as raw Qonfig::Settings objects](#import-nested-settings-as-raw-qonfigsettings-objects)\n    - [Import with pattern-matching](#import-with-pattern-matching)\n    - [Support for predicate-like methods](#support-for-predicate-like-methods)\n  - [Export config settings](#export-config-settings) (`as singleton methods`)\n- [Validation](#validation)\n  - [Introduction](#introduction)\n  - [Key search pattern](#key-search-pattern)\n  - [Proc-based validation](#proc-based-validation)\n  - [Method-based validation](#method-based-validation)\n  - [Predefined validations](#predefined-validations)\n  - [Custom predefined validators](#custom-predefined-validators)\n  - [Validation of potential setting values](#validation-of-potential-setting-values)\n- [Work with files](#work-with-files)\n  - **Setting keys definition**\n    - `(DSL methods for dynamic setting keys definition by reading them from a file)`\n    - [Load from YAML file](#load-from-yaml-file)\n    - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)\n    - [Load from JSON file](#load-from-json-file)\n    - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)\n    - [Load from ENV](#load-from-env)\n    - [Load from \\_\\_END\\_\\_](#load-from-__end__) (aka `.load_from_self`)\n    - [Expose \\_\\_END\\_\\_](#expose-__end__) (aka `.expose_self`)\n  - **Setting values**\n    - `(instance methods for loading the setting values from a file to existing config object with already defined setting keys)`\n    - [Default setting values file](#default-setting-values-file)\n    - [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)\n    - [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)\n    - [Load setting values from \\_\\_END\\_\\_](#load-setting-values-from-__end__-by-instance)\n    - [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)\n  - **Daily work**\n    - [Save to JSON file](#save-to-json-file) (`#save_to_json`)\n    - [Save to YAML file](#save-to-yaml-file) (`#save_to_yaml`)\n- [Plugins](#plugins)\n  - [toml](#plugins-toml) (support for `TOML` format)\n  - [pretty_print](#plugins-pretty_print) (beautified/prettified console output)\n  - [vault](#plugins-vault) (support for `Vault` store)\n- [Roadmap](#roadmap)\n- [Build](#build)\n---\n\n## Definition\n\n- [Definition and Settings Access](#definition-and-access)\n- [Configuration](#configuration)\n- [Inheritance](#inheritance)\n- [Composition](#composition)\n- [Hash representation](#hash-representation)\n- [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)\n\n---\n\n### Definition and Access\n\n- `setting(name, value = nil)` - define setting with corresponding name and value;\n- `setting(name) { setting(name, value = nil); ... }` - define nested settings OR reopen existing nested setting and define some new nested settings;\n- `re_setting(name, value = nil)`, `re_setting(name) { ... }` - re-define existing setting (or define new if the original does not exist);\n- accessing: [access via method](#access-via-method), [access via index-method \\[\\]](#access-via-index-method-),\n  [.dig](#dig), [.slice](#slice), [.slice_value](#slice_value), [.subset](#subset);\n\n```ruby\n# --- definition ---\nclass Config \u003c Qonfig::DataSet\n  # nil by default\n  setting :project_id\n\n  # nested setting\n  setting :vendor_api do\n    setting :host, 'vendor.service.com'\n  end\n\n  setting :enable_graphql, false\n\n  # nested setting reopening\n  setting :vendor_api do\n    setting :user, 'simple_user'\n  end\n\n  # re-definition of existing setting (drop the old - make the new)\n  re_setting :vendor_api do\n    setting :domain, 'api.service.com'\n    setting :login, 'test_user'\n  end\n\n  # deep nesting\n  setting :credentials do\n    setting :user do\n      setting :login, 'D@iVeR'\n      setting :password, 'test123'\n    end\n  end\nend\n\nconfig = Config.new # your configuration object instance\n```\n\n#### access via method\n\n```ruby\n# get option value via method\nconfig.settings.project_id # =\u003e nil\nconfig.settings.vendor_api.domain # =\u003e 'app.service.com'\nconfig.settings.vendor_api.login # =\u003e 'test_user'\nconfig.settings.enable_graphql # =\u003e false\n```\n\n#### access via index-method []\n\n- without dot-notation:\n\n```ruby\n# get option value via index (with indifferent (string / symbol / mixed) access)\nconfig.settings[:project_id] # =\u003e nil\nconfig.settings[:vendor_api][:domain] # =\u003e 'app.service.com'\nconfig.settings[:vendor_api][:login] # =\u003e 'test_user'\nconfig.settings[:enable_graphql] # =\u003e false\n\n# get option value via index (with indifferent (string / symbol / mixed) access)\nconfig.settings['project_id'] # =\u003e nil\nconfig.settings['vendor_api']['domain'] # =\u003e 'app.service.com'\nconfig.settings['vendor_api']['login'] # =\u003e 'test_user'\nconfig.settings['enable_graphql'] # =\u003e false\n\n# dig to value\nconfig.settings[:vendor_api, :domain] # =\u003e 'app.service.com'\nconfig.settings[:vendor_api, 'login'] # =\u003e 'test_user'\n\n# get option value directly via index (with indifferent access)\nconfig['project_id'] # =\u003e nil\nconfig['enable_graphql'] # =\u003e false\nconfig[:project_id] # =\u003e nil\nconfig[:enable_graphql] # =\u003e false\n```\n\n- with dot-notation:\n\n```ruby\nconfig.settings['vendor_api.domain'] # =\u003e 'app.service.com'\nconfig.settings['vendor_api.login'] # =\u003e 'test_user'\n\nconfig['vendor_api.domain'] # =\u003e 'app.service.com'\nconfig['vendor_api.login'] # =\u003e 'test_user'\n```\n\n#### .dig\n\n- without dot-notation:\n\n```ruby\n# get option value in Hash#dig manner (and fail when the required key does not exist);\nconfig.dig(:vendor_api, :domain) # =\u003e 'app.service.com' # (key exists)\nconfig.dig(:vendor_api, :login) # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n- with dot-notation:\n\n```ruby\nconfig.dig('vendor_api.domain') # =\u003e 'app.service.com' # (key exists)\nconfig.dig('vendor_api.login') # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n#### .slice\n\n- without dot-notation:\n\n```ruby\n# get a hash slice of setting options (and fail when the required key does not exist);\nconfig.slice(:vendor_api) # =\u003e { 'vendor_api' =\u003e { 'domain' =\u003e 'app_service', 'login' =\u003e 'test_user' } }\nconfig.slice(:vendor_api, :login) # =\u003e { 'login' =\u003e 'test_user' }\nconfig.slice(:project_api) # =\u003e Qonfig::UnknownSettingError # (key does not exist)\nconfig.slice(:vendor_api, :port) # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n- with dot-notation:\n\n```ruby\nconfig.slice('vendor_api.login') # =\u003e { 'loign' =\u003e 'test_user' }\nconfig.slice('vendor_api.port') # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n\n#### .slice_value\n\n- without dot-notaiton:\n\n```ruby\n# get value from the slice of setting options using the given key set\n# (and fail when the required key does not exist) (works in slice manner);\n\nconfig.slice_value(:vendor_api) # =\u003e { 'domain' =\u003e 'app_service', 'login' =\u003e 'test_user' }\nconfig.slice_value(:vendor_api, :login) # =\u003e 'test_user'\nconfig.slice_value(:project_api) # =\u003e Qonfig::UnknownSettingError # (key does not exist)\nconfig.slice_value(:vendor_api, :port) # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n- with dot-notation:\n\n```ruby\nconfig.slice_value('vendor_api.login') # =\u003e 'test_user'\nconfig.slice_value('vendor_api.port') # =\u003e Qonfig::UnknownSettingError # (key does not exist)\n```\n\n#### .subset\n\n- without dot-notation:\n\n```ruby\n# - get a subset (a set of sets) of config settings represented as a hash;\n# - each key (or key set) represents a requirement of a certain setting key;\n\nconfig.subset(:vendor_api, :enable_graphql)\n# =\u003e { 'vendor_api' =\u003e { 'login' =\u003e ..., 'domain' =\u003e ... }, 'enable_graphql' =\u003e false }\n\nconfig.subset(:project_id, [:vendor_api, :domain], [:credentials, :user, :login])\n# =\u003e { 'project_id' =\u003e nil, 'domain' =\u003e 'app.service.com', 'login' =\u003e 'D@iVeR' }\n```\n\n- with dot-notation:\n\n```ruby\nconfig.subset('project_id', 'vendor_api.domain', 'credentials.user.login')\n# =\u003e { 'project_id' =\u003e nil, 'domain' =\u003e 'app.service.com', 'login' =\u003e 'D@iVeR' }\n```\n\n---\n\n### Configuration\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :testing do\n    setting :engine, :rspec\n    setting :parallel, true\n  end\n\n  setting :geo_api do\n    setting :provider, :google_maps\n  end\n\n  setting :enable_middlewares, false\nend\n\nconfig = Config.new\n```\n\n#### configure via proc\n\n```ruby\nconfig.configure do |conf|\n  conf.enable_middlewares = true\n  conf.geo_api.provider = :yandex_maps\n  conf.testing.engine = :mini_test\nend\n```\n\n#### configure via settings object (by option name)\n\n```ruby\nconfig.settings.enable_middlewares = false\nconfig.settings.geo_api.provider = :apple_maps\nconfig.settings.testing.engine = :ultra_test\n```\n\n#### configure via settings object (by setting key)\n\n```ruby\nconfig.settings[:enable_middlewares] = true\nconfig.settings[:geo_api][:provider] = :rambler_maps\nconfig.settings[:testing][:engine] = :mega_test\n```\n\n#### instant configuration via proc\n\n```ruby\nconfig = Config.new do |conf|\n  conf.enable_middlewares = false\n  conf.geo_api.provider = :amazon_maps\n  conf.testing.engine = :crypto_test\nend\n```\n\n#### using a hash\n\n```ruby\nconfig = Config.new(\n  testing: { engine: :mini_test, parallel: false },\n  geo_api: { provider: :rambler_maps },\n  enable_middlewares: true\n)\nconfig.configure(enable_middlewares: false)\n```\n\n#### using both hash and proc (proc has higher priority)\n\n```ruby\nconfig = Config.new(enable_middlewares: true) do |conf|\n  conf.testing.parallel = true\nend\n\nconfig.configure(geo_api: { provider: nil }) do |conf|\n  conf.testing.engine = :rspec\nend\n```\n\n---\n\n### Inheritance\n\n```ruby\nclass CommonConfig \u003c Qonfig::DataSet\n  setting :uploader, :fog\nend\n\nclass ProjectConfig \u003c CommonConfig\n  setting :auth_provider, :github\nend\n\nproject_config = ProjectConfig.new\n\n# inherited setting\nproject_config.settings.uploader # =\u003e :fog\n\n# own setting\nproject_config.settings.auth_provider # =\u003e :github\n```\n\n---\n\n### Composition\n\n```ruby\nclass SharedConfig \u003c Qonfig::DataSet\n  setting :logger, Logger.new\nend\n\nclass ServerConfig \u003c Qonfig::DataSet\n  setting :port, 12345\n  setting :address, '0.0.0.0'\nend\n\nclass DatabaseConfig \u003c Qonfig::DataSet\n  setting :user, 'test'\n  setting :password, 'testpaswd'\nend\n\nclass ProjectConfig \u003c Qonfig::DataSet\n  compose SharedConfig\n\n  setting :server do\n    compose ServerConfig\n  end\n\n  setting :db do\n    compose DatabaseConfig\n  end\nend\n\nproject_config = ProjectConfig.new\n\n# fields from SharedConfig\nproject_config.settings.logger # =\u003e #\u003cLogger:0x66f57048\u003e\n\n# fields from ServerConfig\nproject_config.settings.server.port # =\u003e 12345\nproject_config.settings.server.address # =\u003e '0.0.0.0'\n\n# fields from DatabaseConfig\nproject_config.settings.db.user # =\u003e 'test'\nproject_config.settings.db.password # =\u003e 'testpaswd'\n```\n\n---\n\n### Hash representation\n\n- works via `#to_h` and `#to_hash`;\n- supported options:\n  - `key_transformer:` - an optional proc that accepts setting key and makes your custom transformations;\n  - `value_transformer:` - an optional proc that accepts setting value and makes your custom transformations;\n  - `dot_style:` - (`false` by default) represent setting keys in dot-notation (transformations are supported too);\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :serializers do\n    setting :json do\n      setting :engine, :ok\n    end\n\n    setting :hash do\n      setting :engine, :native\n    end\n  end\n\n  setting :adapter do\n    setting :default, :memory_sync\n  end\n\n  setting :logger, Logger.new(STDOUT)\nend\n```\n\n#### Default behavior (without-options)\n\n```ruby\nConfig.new.to_h\n# =\u003e\n{\n  \"serializers\": {\n    \"json\" =\u003e { \"engine\" =\u003e :ok },\n    \"hash\" =\u003e { \"engine\" =\u003e :native },\n  },\n  \"adapter\" =\u003e { \"default\" =\u003e :memory_sync },\n  \"logger\" =\u003e #\u003cLogger:0x4b0d79fc\u003e\n}\n```\n\n#### With transformations\n\n- with `key_transformer` and/or `value_transformer`;\n\n```ruby\nkey_transformer = -\u003e (key) { \"#{key}!!\" }\nvalue_transformer = -\u003e (value) { \"#{value}??\" }\n\nConfig.new.to_h(key_transformer: key_transformer, value_transformer: value_transformer)\n# =\u003e\n{\n  \"serializers!!\": {\n    \"json!!\" =\u003e { \"engine!!\" =\u003e \"ok??\" },\n    \"hash!!\" =\u003e { \"engine!!\" =\u003e \"native??\" },\n  },\n  \"adapter!!\" =\u003e { \"default!!\" =\u003e \"memory_sync??\" },\n  \"logger!!\" =\u003e \"#\u003cLogger:0x00007fcde799f158\u003e??\"\n}\n```\n\n#### Dot-style format\n\n- transformations are supported too (`key_transformer` and `value_transformer`);\n\n```ruby\nConfig.new.to_h(dot_style: true)\n# =\u003e\n{\n  \"serializers.json.engine\" =\u003e :ok,\n  \"serializers.hash.engine\" =\u003e :native,\n  \"adapter.default\" =\u003e :memory_sync,\n  \"logger\" =\u003e #\u003cLogger:0x4b0d79fc\u003e,\n}\n```\n\n```ruby\ntransformer = -\u003e (value) { \"$$#{value}$$\" }\n\nConfig.new.to_h(dot_style: true, key_transformer: transformer, value_transformer: transformer)\n\n# =\u003e \"#\u003cLogger:0x00007fcde799f158\u003e??\"\n{\n  \"$$serializers.json.engine$$\" =\u003e \"$$ok$$\",\n  \"$$serializers.hash.engine$$\" =\u003e \"$$native$$\",\n  \"$$adapter.default$$\" =\u003e \"$$memory_sync$$\",\n  \"$$logger$$\" =\u003e \"$$#\u003cLogger:0x00007fcde799f158\u003e$$\",\n}\n```\n\n---\n\n### Smart Mixin\n\n- class-level:\n  - `.configuration` - settings definitions;\n  - `.configure` - configuration;\n  - `.config` - config object;\n  - settings definitions are inheritable;\n- instance-level:\n  - `#configure` - configuration;\n  - `#config` - config object;\n  - `#shared_config` - class-level config object;\n\n```ruby\n# --- usage ---\n\nclass Application\n  # make configurable\n  include Qonfig::Configurable\n\n  configuration do\n    setting :user\n    setting :password\n  end\nend\n\napp = Application.new\n\n# class-level config\nApplication.config.settings.user # =\u003e nil\nApplication.config.settings.password # =\u003e nil\n\n# instance-level config\napp.config.settings.user # =\u003e nil\napp.config.settings.password # =\u003e nil\n\n# access to the class level config from an instance\napp.shared_config.settings.user # =\u003e nil\napp.shared_config.settings.password # =\u003e nil\n\n# class-level configuration\nApplication.configure do |conf|\n  conf.user = '0exp'\n  conf.password = 'test123'\nend\n\n# instance-level configuration\napp.configure do |conf|\n  conf.user = 'admin'\n  conf.password = '123test'\nend\n\n# class has own config object\nApplication.config.settings.user # =\u003e '0exp'\nApplication.config.settings.password # =\u003e 'test123'\n\n# instance has own config object\napp.config.settings.user # =\u003e 'admin'\napp.config.settings.password # =\u003e '123test'\n\n# access to the class level config from an instance\napp.shared_config.settings.user # =\u003e '0exp'\napp.shared_config.settings.password # =\u003e 'test123'\n\n# and etc... (all Qonfig-related features)\n```\n\n```ruby\n# --- inheritance ---\n\nclass BasicApplication\n  # make configurable\n  include Qonfig::Configurable\n\n  configuration do\n    setting :user\n    setting :pswd\n  end\n\n  configure do |conf|\n    conf.user = 'admin'\n    conf.pswd = 'admin'\n  end\nend\n\nclass GeneralApplication \u003c BasicApplication\n  # extend inherited definitions\n  configuration do\n    setting :db do\n      setting :adapter\n    end\n  end\n\n  configure do |conf|\n    conf.user = '0exp' # .user inherited from BasicApplication\n    conf.pswd = '123test' # .pswd inherited from BasicApplication\n    conf.db.adapter = 'pg'\n  end\nend\n\nBasicApplication.config.to_h\n{ 'user' =\u003e 'admin', 'pswd' =\u003e 'admin' }\n\nGeneralApplication.config.to_h\n{ 'user' =\u003e '0exp', 'pswd' =\u003e '123test', 'db' =\u003e { 'adapter' =\u003e 'pg' } }\n\n# and etc... (all Qonfig-related features)\n```\n\n---\n\n### Instantiation without class definition\n\n- without inheritance:\n\n```ruby\nconfig = Qonfig::DataSet.build do\n  setting :user, 'D@iVeR'\n  setting :password, 'test123'\n\n  def custom_method\n    'custom_result'\n  end\nend\n\nconfig.is_a?(Qonfig::DataSet) # =\u003e true\n\nconfig.settings.user # =\u003e 'D@iVeR'\nconfig.settings.password # =\u003e 'test123'\nconfig.custom_method # =\u003e 'custom_result'\n```\n\n- with inheritance:\n\n```ruby\nclass GeneralConfig \u003c Qonfig::DataSet\n  setting :db_adapter, :postgresql\nend\n\nconfig = Qonfig::DataSet.build(GeneralConfig) do\n  setting :web_api, 'api.google.com'\nend\n\nconfig.is_a?(Qonfig::DataSet) # =\u003e true\n\nconfig.settings.db_adapter # =\u003e :postgresql\nconfig.settings.web_api # =\u003e \"api.google.com\"\n```\n\n---\n\n## Compacted config\n\n- [Definition and instantiation](#definition-and-instantiation)\n  - [by raw initialization](#by-raw-initialization)\n  - [by existing Qonfig::DataSet class](#by-existing-qonfigdataset-class)\n  - [by existing Qonfig::DataSet instance](#by-existing-qonfigdataset-instance)\n  - [instantiation without class definition](#instantiation-without-class-definition-1)\n  - [validation API](#validation-api-see-full-documentation)\n- [Setting readers and writers](#setting-readers-and-writers)\n  - [reading](#reading-by-setting-name-and-index-method-with-dot-notation-support-and-indifferent-access)\n  - [writing](#writing-by-setting-name-and-index-method-with-dot-notation-support-and-indifferent-access)\n  - [precitaes](#predicates-see-full-documentation)\n\n---\n\n- `Qonfig::Compacted`: represents the compacted config object with setting readers, setting writers and setting predicates only - and no any other useful instance-based functionality:\n- setting keys are represented as direct instace methods (`#settings` invokation does not need);\n- support for index-like access methods (`[]`,`[]=`);\n- full support of `Qonfig::DataSet` definition DSL commands:\n  - `setting`, `re_setting` [doc](#definition-and-access)\n  - `validate`, `add_validator` [doc](#validation)\n  - `load_from_self` [doc](#load-from-__end__), `load_from_yaml` [doc](#load-from-yaml-file), `load_from_json` [doc](#load-from-json-file), `load_from_toml` [doc](#plugins-toml);\n  - `expose_self` [doc](#expose-__end__), `expose_yaml` [doc](#expose-yaml), `expose_json` [doc](#expose-json), `expose_toml` [doc](#plugins-toml)\n  - `values_file` [doc](#default-setting-values-file)\n- support for validation of potential setting values `.valid_with?` [documentation](#validation-of-potential-setting-values);\n- can be instantiated by:\n  - by existing config object: `Qonfig::DataSet#compacted` or `Qonfig::Compacted.build_from(config, \u0026configuration)`;\n  - from existing `Qonfig::DataSet` class: `Qonfig::DataSet.build_compacted`;\n  - by direct instantiation: `Qonfig::Compacted.new(settings_values = {}, \u0026configuration)`;\n  - by implicit instance building without explicit class definition `Qonfig::Compacted.build(\u0026dsl_commands)`;\n- you can define your own instance methods too;\n\n---\n\n### Definition and instantiation\n\n#### by raw initialization\n\n```ruby\nclass Config \u003c Qonfig::Compacted\n  setting :api, 'google.com'\n  setting :enabled, true\n  setting :queue do\n    setting :engine, :sidekiq\n  end\nend\n\nconfig = Config.new(api: 'yandex.ru') do |conf|\n  conf.enabled = false\nend\n\nconfig.api # =\u003e 'yandex.ru'\nconfig.enabled # =\u003e false\nconfig.queue.engine # =\u003e :sidekiq\n```\n\n#### by existing Qonfig::DataSet class\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :api, 'google.com'\n  setting :enabled, true\nend\n\nconfig = Config.build_compacted # builds Qonfig::Compacted instance\n\nconfig.api # =\u003e 'google.com'\nconfig.enabled # =\u003e true\n```\n\n#### by existing Qonfig::DataSet instance\n\n- `Qonfig::DataSet#compacted`\n- (or) `Qonfig::Compacted.build_from(config)`\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :api, 'google.com'\n  setting :enabled, true\nend\n\nconfig = Config.new\n\ncompacted_config = config.compacted\n# --- or ---\ncompacted_config = Qonfig::Compacted.build_from(config)\n\ncompacted_config.api # =\u003e 'google.com'\ncompacted_config.enabled # =\u003e true\n```\n\n#### instantiation without class definition\n\n```ruby\nconfig = Qonfig::Compacted.build do\n  setting :api, 'google.ru'\n  setting :enabled, true\nend\n\nconfig.api # =\u003e 'google.ru'\nconfig.enabled # =\u003e true\n```\n\n#### validation API (see [full documentation](#validation)):\n\n```ruby\n# custom validators\nQonfig::Compacted.define_validator(:version_check) do |value|\n  value.is_a?(Integer) \u0026\u0026 value \u003c 100\nend\n\nclass Config \u003c Qonfig::Compacted\n  setting :api, 'google.ru'\n  setting :enabled, true\n  setting :version, 2\n  setting :queue { setting :engine, :sidekiq }\n\n  # full support of original validation api\n  validate :api, :string, strict: true\n  validate :enabled, :boolean, strict: true\n  validate :version, :version_check # custom validator\n  validate 'queue.#', :symbol\nend\n\n# potential values validation\nConfig.valid_with?(api: :yandex) # =\u003e false\nConfig.valid_with?(enabled: nil) # =\u003e false\nConfig.valid_with?(version: nil) # =\u003e false\nConfig.valid_with?(api: 'yandex.ru', enabled: false, version: 3) # =\u003e true\n\nconfig = Config.new\n\n# instance validation\nconfig.api = :yandex # =\u003e Qonfig::ValidationError (should be a type of string)\nconfig.version = nil # =\u003e Qonfig::ValidationError (can not be nil)\nconfig.queue.engine = 'sneakers' # =\u003e Qonfig::ValidationError (should be a type of symbol)\n```\n\n---\n\n### Setting readers and writers\n\n```ruby\nclass Config \u003c Qonfig::Compcated\n  setting :api, 'google.ru'\n  setting :enabled, true\n  setting :queue do\n    setting :engine, :sidekiq\n    setting :workers_count, 10\n  end\nend\n\nconfig = Config.new\n```\n\n#### reading (by setting name and index method with dot-notation support and indifferent access)\n\n```ruby\n# by setting name\nconfig.api # =\u003e 'google.ru'\nconfig.enabled # =\u003e true\nconfig.queue.engine # =\u003e :sidekiq\nconfig.queue.workers_count # =\u003e 10\n\n# by index method with dot-notation support and indiffernt access\nconfig[:api] # =\u003e 'google.ru'\nconfig['enabled'] # =\u003e true\nconfig[:queue][:engine] # =\u003e :sidekiq\nconfig['queue.workers_count'] # =\u003e 10\n```\n\n#### writing (by setting name and index method with dot-notation support and indifferent access)\n\n```ruby\n# by setting name\nconfig.api = 'yandex.ru'\nconfig.queue.engine = :sidekiq\n# and etc\n\n# by index method with dot-notaiton support and indifferent access\nconfig['api'] = 'yandex.ru'\nconfig['queue.engine'] = :sidekiq\nconfig[:queue][:workers_count] = 5\n```\n\n#### predicates ([see full documentation](#settings-as-predicates))\n\n```ruby\nclass Config \u003c Qonfig::Compcated\n  setting :enabled, true\n  setting :api, 'yandex.ru'\n  setting :queue do\n    setting :engine, :sidekiq\n  end\nend\n\nconfig = Config.new\n\nconfig.enabled? # =\u003e true\nconfig.enabled = nil\nconfig.enabled? # =\u003e false\n\nconfig.queue.engine? # =\u003e true\nconfig.queue.engine =  nil\nconfig.queue.engine? # =\u003e false\n\nconfig.queue? # =\u003e true\n```\n\n---\n\n## Interaction\n\n- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)\n- [List of config keys](#list-of-config-keys) (`#keys`, `#root_keys`)\n- [Config reloading](#config-reloading) (reload config definitions and option values)\n- [Clear options](#clear-options) (set to `nil`)\n- [Frozen state](#frozen-state) (`.freeze_state!`, `#freeze!`, `#frozen?`)\n- [Settings as Predicates](#settings-as-predicates)\n- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)\n- [Run arbitrary code with temporary settings](#run-arbitrary-code-with-temporary-settings)\n\n---\n\n### Iteration over setting keys\n\n- `#each_setting { |key, value| }`\n  - iterates over the root setting keys;\n- `#deep_each_setting(yield_all: false) { |key, value| }`\n  - iterates over all setting keys (deep inside);\n  - key object is represented as a string of `.`-joined setting key names;\n  - `yield_all:` means \"yield all config objects\" (end values and root setting objects those have nested settings) (`false` by default);\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :db do\n    setting :creds do\n      setting :user, 'D@iVeR'\n      setting :password, 'test123',\n      setting :data, test: false\n    end\n  end\n\n  setting :telegraf_url, 'udp://localhost:8094'\n  setting :telegraf_prefix, 'test'\nend\n\nconfig = Config.new\n```\n\n#### .each_setting\n\n```ruby\nconfig.each_setting { |key, value| { key =\u003e value } }\n\n# result of each step:\n{ 'db' =\u003e \u003cQonfig::Settings:0x00007ff8\u003e }\n{ 'telegraf_url' =\u003e 'udp://localhost:8094' }\n{ 'telegraf_prefix' =\u003e 'test' }\n```\n\n#### .deep_each_setting\n\n```ruby\nconfig.deep_each_setting { |key, value| { key =\u003e value } }\n\n# result of each step:\n{ 'db.creds.user' =\u003e 'D@iveR' }\n{ 'db.creds.password' =\u003e 'test123' }\n{ 'db.creds.data' =\u003e { test: false } }\n{ 'telegraf_url' =\u003e 'udp://localhost:8094' }\n{ 'telegraf_prefix' =\u003e 'test' }\n```\n\n#### .deep_each_setting(yield_all: true)\n\n```ruby\nconfig.deep_each_setting(yield_all: true) { |key, value| { key =\u003e value } }\n\n# result of each step:\n{ 'db' =\u003e \u003cQonfig::Settings:0x00007ff8\u003e } # (yield_all: true)\n{ 'db.creds' =\u003e \u003cQonfig::Settings:0x00002ff1\u003e } # (yield_all: true)\n{ 'db.creds.user' =\u003e 'D@iVeR' }\n{ 'db.creds.password' =\u003e 'test123' }\n{ 'db.crds.data' =\u003e { test: false } }\n{ 'telegraf_url' =\u003e 'udp://localhost:8094' }\n{ 'telegraf_prefix' =\u003e 'test' }\n```\n\n---\n\n### List of config keys\n\n- `#keys` - returns a list of all config keys in dot-notation format;\n  - `all_variants:` - get all possible variants of the config's keys sequences (`false` by default);\n  - `only_root:` - get only the root config keys (`false` by default);\n- `#root_keys` - returns a list of root config keys (an alias for `#keys(only_root: true)`);\n\n```ruby\n# NOTE: suppose we have the following config\n\nclass Config \u003c Qonfig::DataSet\n  setting :credentials do\n    setting :social do\n      setting :service, 'instagram'\n      setting :login, '0exp'\n    end\n\n    setting :admin do\n      setting :enabled, true\n    end\n  end\n\n  setting :server do\n    setting :type, 'cloud'\n    setting :options do\n      setting :os, 'CentOS'\n    end\n  end\nend\n\nconfig = Config.new\n```\n\n#### Default behavior\n\n```ruby\nconfig.keys\n\n# the result:\n[\n  \"credentials.social.service\",\n  \"credentials.social.login\",\n  \"credentials.admin.enabled\",\n  \"server.type\",\n  \"server.options.os\"\n]\n```\n\n#### All key variants\n\n```ruby\nconfig.keys(all_variants: true)\n\n# the result:\n[\n  \"credentials\",\n  \"credentials.social\",\n  \"credentials.social.service\",\n  \"credentials.social.login\",\n  \"credentials.admin\",\n  \"credentials.admin.enabled\",\n  \"server\",\n  \"server.type\",\n  \"server.options\",\n  \"server.options.os\"\n]\n```\n\n#### Only root keys\n\n```ruby\nconfig.keys(only_root: true)\n\n# the result:\n['credentials', 'server']\n```\n\n```ruby\nconfig.root_keys\n\n# the result:\n['credentials', 'server']\n```\n\n---\n\n### Config reloading\n\n- method signature: `#reload!(configurations = {}, \u0026configuration)`;\n\n```ruby\n# -- config example ---\n\nclass Config \u003c Qonfig::DataSet\n  setting :db do\n    setting :adapter, 'postgresql'\n  end\n\n  setting :logger, Logger.new(STDOUT)\nend\n\nconfig = Config.new\n\nconfig.settings.db.adapter # =\u003e 'postgresql'\nconfig.settings.logger # =\u003e #\u003cLogger:0x00007ff9\u003e\n```\n\n```ruby\n# --- redefine some settings (or add a new one) --\n\nconfig.configure { |conf| conf.logger = nil } # redefine some settings (will be reloaded)\n\n# re-define and append settings\nclass Config\n  setting :db do\n    setting :adapter, 'mongoid' # re-define defaults\n  end\n\n  setting :enable_api, false # append new setting\nend\n```\n\n```ruby\n# --- reload ---\n\n# reload settings\nconfig.reload!\n\nconfig.settings.db.adapter # =\u003e 'mongoid'\nconfig.settings.logger # =\u003e #\u003cLogger:0x00007ff9\u003e (reloaded from defaults)\nconfig.settings.enable_api # =\u003e false (new setting)\n\n# reload with instant configuration\nconfig.reload!(db: { adapter: 'oracle' }) do |conf|\n  conf.enable_api = true # changed instantly\nend\n\nconfig.settings.db.adapter # =\u003e 'oracle'\nconfig.settings.logger = # =\u003e #\u003cLogger:0x00007ff9\u003e\nconfig.settings.enable_api # =\u003e true # value from instant change\n```\n\n---\n\n### Clear options\n\n- set all config's settings to `nil`;\n- method signature: `#clear!`;\n\n```ruby\nclass Config\n  setting :database do\n    setting :user\n    setting :password\n  end\n\n  setting :web_api do\n    setting :endpoint\n  end\nend\n\nconfig = Config.new do |conf|\n  conf.database.user = '0exp'\n  conf.database.password = 'test123'\n\n  conf.web_api.endpoint = '/api/'\nend\n\nconfig.settings.database.user # =\u003e '0exp'\nconfig.settings.database.password # =\u003e 'test123'\nconfig.settings.web_api.endpoint # =\u003e '/api'\n\n# clear all options\nconfig.clear!\n\nconfig.settings.database.user # =\u003e nil\nconfig.settings.database.password # =\u003e nil\nconfig.settings.web_api.endpoint # =\u003e nil\n```\n\n---\n\n### Frozen state\n\n#### Instance-level\n\n- method signature: `#freeze!`;\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :logger, Logger.new(STDOUT)\n  setting :worker, :sidekiq\n  setting :db do\n    setting :adapter, 'postgresql'\n  end\nend\n\nconfig = Config.new\nconfig.freeze!\n\nconfig.settings.logger = Logger.new(StringIO.new) # =\u003e Qonfig::FrozenSettingsError\nconfig.settings.worker = :que # =\u003e Qonfig::FrozenSettingsError\nconfig.settings.db.adapter = 'mongoid' # =\u003e Qonfig::FrozenSettingsError\n\nconfig.reload! # =\u003e Qonfig::FrozenSettingsError\nconfig.clear! # =\u003e Qonfig::FrozenSettingsError\n```\n\n#### Definition-level\n\n- DSL-method signature: `freeze_state!`\n- indicaes that all your config instances should be frozen;\n- `freeze_state!` DSL command is not inherited (your child and composed config classes will not have this declaration);\n\n```ruby\n# --- base class ---\nclass Config \u003c Qonfig::DataSet\n  setting :test, true\n  freeze_state!\nend\n\nconfig = Config.new\nconfig.frozen? # =\u003e true\nconfig.settings.test = false # =\u003e Qonfig::FrozenSettingsError\n\n# --- child class ---\nclass InheritedConfig \u003c Config\nend\n\ninherited_config = InheritedConfig.new\nconfig.frozen? # =\u003e false\nconfig.settings.test = false # ok :)\n```\n\n---\n\n### Settings as Predicates\n\n- predicate form: `?` at the end of setting name;\n- `nil` and `false` setting values indicates `false`;\n- other setting values indicates `true`;\n- setting roots always returns `true`;\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :database do\n    setting :user\n    setting :host, 'google.com'\n\n    setting :engine do\n      setting :driver, 'postgres'\n    end\n  end\nend\n\nconfig = Config.new\n\n# predicates\nconfig.settings.database.user? # =\u003e false (nil =\u003e false)\nconfig.settings.database.host? # =\u003e true ('google.com' =\u003e true)\nconfig.settings.database.engine.driver? # =\u003e true ('postgres' =\u003e true)\n\n# setting roots always returns true\nconfig.settings.database? # =\u003e true\nconfig.settings.database.engine? # =\u003e ture\n\nconfig.configure do |conf|\n  conf.database.user = '0exp'\n  conf.database.host = false\n  conf.database.engine.driver = true\nend\n\n# predicates\nconfig.settings.database.user? # =\u003e true ('0exp' =\u003e true)\nconfig.settings.database.host? # =\u003e false (false =\u003e false)\nconfig.settings.database.engine.driver? # =\u003e true (true =\u003e true)\n```\n\n---\n\n### Setting key existence\n\n- supports **dynamic array-like format** and **canonical dot-notation format**;\n- returns `true` if the concrete key is exist;\n- returns `false` if the concrete key does not exist;\n- **dynamic array-like format**:\n  - `#key?(*key_path)` / `#option?(*key_path)` / `#setting?(*key_path)`;\n  - `*key_path` - an array of symbols and strings that represents a path to the concrete setting key;\n  - (for example, `config.key?(:credentials, :user)` tries to check that `config.settings.credentials.user` is exist);\n- **dot-notation format**:\n  - `#key?(key)` / `#option?(key)` / `#setting?(key)`;\n  - `key` - string in dot-notated format\n  - (for example: `config.key?('credentials.user')` tries to check that `config.settings.crednetials.user` is exist);\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :credentials do\n    setting :user, 'D@iVeR'\n    setting :password, 'test123'\n  end\nend\n\nconfig = Config.new\n\n# --- array-like format ---\nconfig.key?('credentials', 'user') # =\u003e true\nconfig.key?('credentials', 'token') # =\u003e false (key does not exist)\n\n# --- dot-notation format ---\nconfig.key?('credentials.user') # =\u003e true\nconfig.key?('credentials.token') # =\u003e false (key does not exist)\n\nconfig.key?('credentials') # =\u003e true\nconfig.key?('que_adapter') # =\u003e false (key does not exist)\n\n# aliases\nconfig.setting?('credentials') # =\u003e true\nconfig.option?(:credentials, :password) # =\u003e true\nconfig.option?('credentials.password') # =\u003e true\n```\n\n---\n\n### Run arbitrary code with temporary settings\n\n- provides a way to run an arbitrary code with temporarily specified settings;\n- your arbitrary code can temporary change any setting too - all settings will be returned to the original state;\n- (it is convenient to run code samples by this way in tests (with substitued configs));\n- it is fully thread-safe `:)`;\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :queue do\n    setting :adapter, :sidekiq\n    setting :options, {}\n  end\nend\n\nconfig = Config.new\n\n# run a block of code with temporary queue.adapter setting\nconfig.with(queue: { adapter: 'que' }) do\n  # your changed settings\n  config.settings.queue.adapter # =\u003e 'que'\n\n  # you can temporary change settings by your code too\n  config.settings.queue.options = { concurrency: 10 }\n\n  # ...your another code...\nend\n\n# original settings has not changed :)\nconfig.settings.queue.adapter # =\u003e :sidekiq\nconfig.settings.queue.options # =\u003e {}\n```\n\n---\n\n## Import settings / Export settings\n\n- [Import config settings](#import-config-settings) (`as instance methods`)\n- [Export config settings](#export-config-settings) (`as singleton methods`)\n\nSometimes the nesting of configs in your project is quite high, and it makes you write the rather \"cumbersome\" code\n(`config.settings.web_api.credentials.account.auth_token` for example). Frequent access to configs in this way is inconvinient - so developers wraps\nsuch code by methods or variables. In order to make developer's life easer `Qonfig` provides a special Import API simplifies the config importing\n(gives you `.import_settings` DSL) and gives an ability to instant config setting export from a config object (gives you `#export_settings` config's method).\n\nYou can use RabbitMQ-like pattern matching in setting key names:\n  - if the setting key name at the current nesting level does not matter - use `*`;\n  - if both the setting key name and nesting level does not matter - use `#`\n  - examples:\n    - `db.settings.user` - matches to `db.settings.user` setting;\n    - `db.settings.*` - matches to all setting keys inside `db.settings` group of settings;\n    - `db.*.user` - matches to all `user` setting keys at the first level of `db` group of settings;\n    - `#.user` - matches to all `user` setting keys;\n    - `service.#.password` - matches to all `password` setting keys at all levels of `service` group of settings;\n    - `#` - matches to ALL setting keys;\n    - `*` - matches to all setting keys at the root level;\n    - and etc;\n\n---\n\n### Import config settings\n\n- `Qonfig::Imports` - a special mixin that provides the convenient DSL to work with config import features (`.import_settings` method);\n- `.import_settings` - DSL method for importing configuration settings (from a config instance) as instance methods of a class;\n- (**IMPORTANT**) `import_settings` imports config settings as access methods to config's settings (creates `attr_reader`s for your config);\n- generated methods can be used as predicates (with trailing `?` symbol);\n- you can generate `attr_accessor`s by specifying `accessor: true` option\n  (be careful: you can get `Qonfig::AmbiguousSettingValueError` when you try to assign a value to config option which have nested settings);\n- signature: `.import_settings(config_object, *setting_keys, mappings: {}, prefix: '', raw: false)`\n  - `config_object` - an instance of `Qonfig::DataSet` whose config settings should be imported;\n  - `*setting_keys` - an array of dot-notaed config's setting keys that should be imported\n    (dot-notaed key is a key that describes each part of nested setting key as a string separated by `dot`-symbol);\n    - last part of dot-notated key will become a name of the setting access instance method;\n  - `mappings:` - a map of keys that describes custom method names for each imported setting;\n  - `prefix:` - prexifies setting access method name with custom prefix;\n  - `raw:` - use nested settings as objects or hashify them (`false` by default (means \"hashify nested settings\"));\n  - `accessor:` - generate `attr_accessor` for imported config settigns (`false` by default (means \"generate `attr_reader`s only\"));\n---\n\nSuppose we have a config with deeply nested keys:\n\n```ruby\n# NOTE: (Qonfig::DataSet.build creates a class and instantly instantiates it)\nAppConfig = Qonfig::DataSet.build do\n  setting :web_api do\n    setting :credentials do\n      setting :account do\n        setting :login, 'DaiveR'\n        setting :auth_token, 'IAdkoa0@()1239uA'\n      end\n    end\n  end\n\n  setting :graphql_api, false\nend\n```\n\nLet's see what we can to do :)\n\n- [Import a set of setting keys (simple dot-noated key list)](#import-a-set-of-setting-keys-simple-dot-noated-key-list)\n- [Import with custom method names (mappings)](#import-with-custom-method-names-mappings)\n- [Prexify method name](#prexify-method-name)\n- [Import nested settings as raw Qonfig::Settings objects](#import-nested-settings-as-raw-qonfigsettings-objects)\n- [Import with pattern-matching](#import-with-pattern-matching)\n- [Support for predicate-like methods](#support-for-predicate-like-methods)\n\n#### Import a set of setting keys (simple dot-noated key list)\n\n- last part of dot-notated key will become a name of the setting access instance method;\n\n```ruby\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig,\n    'web_api.credentials.account.login',\n    'web_api.credentials.account'\n  )\nend\n\nservice = ServiceObject.new\n\nservice.login # =\u003e \"D@iVeR\"\nservice.account # =\u003e { \"login\" =\u003e \"D@iVeR\", \"auth_token\" =\u003e IAdkoa0@()1239uA\" }\n```\n\n#### Import with custom method names (mappings)\n\n- `mappings:` defines a map of keys that describes custom method names for each imported setting;\n\n```ruby\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig, mappings: {\n    account_data: 'web_api.credentials.account', # NOTE: name access method with \"account_data\"\n    secret_token: 'web_api.credentials.account.auth_token' # NOTE: name access method with \"secret_token\"\n  })\nend\n\nservice = ServiceObject.new\n\nservice.account_data # =\u003e { \"login\" =\u003e \"D@iVeR\", \"auth_token\" =\u003e \"IAdkoa0@()1239uA\" }\nservice.auth_token # =\u003e \"IAdkoa0@()1239uA\"\n```\n\n#### Prexify method name\n\n- `prefix:` - prexifies setting access method name with custom prefix;\n\n```ruby\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig,\n    'web_api.credentials.account',\n    mappings: { secret_token: 'web_api.credentials.account.auth_token' },\n    prefix: 'config_'\n  )\nend\n\nservice = ServiceObject.new\n\nservice.config_account # =\u003e { login\" =\u003e \"D@iVeR\", \"auth_token\" =\u003e \"IAdkoa0@()1239uA\" }\nservice.config_secret_token # =\u003e \"IAdkoa0@()1239uA\"\n```\n\n#### Support for predicate-like methods\n\n- generated methods can be used as predicates (with trailing `?` symbol);\n\n```ruby\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig,\n    'web_api.credentials.account',\n    mappings: { secret_token: 'web_api.credentials.account.auth_token' },\n  )\nend\n\nservice = ServiceObject.new\n\nservice.account? # =\u003e true\nservice.secret_token? # =\u003e true\n```\n\n#### Import nested settings as raw Qonfig::Settings objects\n\n- `raw: false` is used by default (hashify nested settings)\n\n```ruby\n# NOTE: import nested settings as raw objects (raw: true)\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig, 'web_api.credentials', raw: true)\nend\n\nservice = ServiceObject.new\n\nservice.credentials # =\u003e \u003cQonfig::Settings:0x00007ff8\u003e\nservice.credentials.account.login # =\u003e \"D@iVeR\"\nservice.credentials.account.auth_token # =\u003e \"IAdkoa0@()1239uA\"\n```\n\n```ruby\n# NOTE: import nested settings as converted-to-hash objects (raw: false) (default behavior)\nclass ServiceObject\n  include Qonfig::Imports\n\n  import_settings(AppConfig, 'web_api.credentials', raw: false)\nend\n\nservice = ServiceObject.new\n\nservice.credentials # =\u003e { \"account\" =\u003e { \"login\" =\u003e \"D@iVeR\", \"auth_token\" =\u003e \"IAdkoa0@()1239uA\"} }\n```\n\n#### Import with pattern-matching\n\n- import root keys only: `import_settings(config_object, '*')`;\n- import all keys: `import_settings(config_object, '#')`;\n- import the subset of keys: `import_settings(config_object, 'group.*.group.#')` (pattern-mathcing usage);\n\n```ruby\nclass ServiceObject\n  include Qonfig::Imports\n\n  # import all settings from web_api.credentials subset\n  import_settings(AppConfig, 'web_api.credentials.#')\n  # generated instance methods:\n  #   =\u003e service.account\n  #   =\u003e service.login\n  #   =\u003e service.auth_token\n\n  # import only the root keys from web_api.credentials.account subset\n  import_settings(AppConfig, 'web_api.credentials.account.*')\n  # generated instance methods:\n  #   =\u003e service.login\n  #   =\u003e service.auth_token\n\n  # import only the root keys\n  import_settings(AppConfig, '*')\n  # generated instance methods:\n  #   =\u003e service.web_api\n  #   =\u003e service.graphql_api\n\n  # import ALL keys\n  import_settings(AppConfig, '#')\n  # generated instance methods:\n  #   =\u003e service.web_api\n  #   =\u003e service.credentials\n  #   =\u003e service.account\n  #   =\u003e service.login\n  #   =\u003e service.auth_token\n  #   =\u003e service.graphql_api\nend\n```\n\n---\n\n### Export config settings\n\n- works in `.import_settings` manner [doc](#import-config-settings) (see examples and documentation above `:)`)\n- all config objects can export their settings to an arbitrary object as singleton methods;\n- (**IMPORTANT**) `export_settings` exports config settings as access methods to config's settings (creates `attr_reader`s for your config);\n- generated methods can be used as predicates (with trailing `?` symbol);\n- you can generate `attr_accessor`s by specifying `accessor: true` option\n  (be careful: you can get `Qonfig::AmbiguousSettingValueError` when you try to assign a value to config option which have nested settings);\n- signature: `#export_settings(exportable_object, *setting_keys, mappings: {}, prefix: '', raw: false)`:\n  - `exportable_object` - an arbitrary object for exporting;\n  - `*setting_keys` - an array of dot-notaed config's setting keys that should be exported\n    (dot-notaed key is a key that describes each part of nested setting key as a string separated by `dot`-symbol);\n    - last part of dot-notated key will become a name of the setting access instance method;\n  - `mappings:` - a map of keys that describes custom method names for each exported setting;\n  - `prefix:` - prexifies setting access method name with custom prefix;\n  - `raw:` - use nested settings as objects or hashify them (`false` by default (means \"hashify nested settings\"));\n - `accessor:` - generate `attr_accessor` for imported config settigns (`false` by default (means \"generate `attr_reader`s only\"));\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :web_api do\n    setting :credentials do\n      setting :account do\n        setting :login, 'DaiveR'\n        setting :auth_token, 'IAdkoa0@()1239uA'\n      end\n    end\n  end\n\n  setting :graphql_api, false\nend\n\nclass ServiceObject; end\n\nconfig = Config.new\nservice = ServiceObject.new\n\nservice.config_account # =\u003e NoMethodError\n```\n\n```ruby\n# NOTE: export settings as access methods to config's settings\nconfig.export_settings(service, 'web_api.credentials.account', prefix: 'config_')\n\nservice.config_account # =\u003e { \"login\" =\u003e \"D@iVeR\", \"auth_token\" =\u003e \"IAdkoa0@()1239uA\" }\n```\n\n```ruby\n# NOTE: export settings with pattern matching\nconfig.export_settings(service, '*') # export root settings\n\nservice.web_api # =\u003e { 'credentials' =\u003e { 'account' =\u003e { ... } }, 'graphql_api' =\u003e false }\nservice.graphql_api # =\u003e false\n```\n\n```ruby\n# NOTE: predicates\nconfig.export_settings(service, '*')\n\nconfig.web_api? # =\u003e true\nconfig.graphql_api? # =\u003e false\n```\n\n---\n\n## Validation\n\n- [Introduction](#introduction)\n- [Key Search Pattern](#key-search-pattern)\n- [Proc-based validation](#proc-based-validation)\n- [Method-based validation](#method-based-validation)\n- [Predefined validations](#predefined-validations)\n- [Custom predefined validators](#custom-predefined-validators)\n- [Validation of potential setting values](#validation-of-potential-setting-values)\n\n---\n\n### Introduction\n\nQonfig provides a lightweight DSL for defining validations and works in all cases when setting values are initialized or mutated.\nSettings are validated as keys (matched with a [specific string pattern](#key-search-pattern)).\nYou can validate both a set of keys and each key separately.\nIf you want to check the config object completely you can define a custom validation.\n\n**Features**:\n- validation is invoked on any mutation of any setting:\n  - during dataset instantiation;\n  - when assigning new values;\n  - when calling `#reload!`;\n  - when calling `#clear!`;\n- provides `strict` and `non-strict` behavior (`strict: true` and `strict: false` respectively):\n  - `strict: false` ignores validations for settings with `nil` (allows `nil` value);\n  - `strict: true` does not ignores validations for settings with `nil`;\n  - `strict: false` is used by default;\n- provides a special [key search pattern](#key-search-pattern) for matching setting key names;\n- you can validate potential setting values without any assignment ([documentation](#validation-of-potential-setting-values))\n- uses the [key search pattern](#key-search-pattern) for definging what the setting key should be validated;\n- you can define your own custom validation logic and validate dataset instance completely;\n- validation logic should return **truthy** or **falsy** value;\n- supprots two validation techniques (**proc-based** ([documentation](#proc-based-validation)) and **dataset-method-based** ([documentation](#method-based-validation))):\n  - **proc-based** (`setting validation`) ([documentation](#proc-based-validation))\n    ```ruby\n      validate('db.user', strict: true) do |value|\n        value.is_a?(String)\n      end\n    ```\n  - **proc-based** (`dataset validation`) ([doc](#proc-based-validation))\n    ```ruby\n      validate(strict: false) do\n        settings.user == User[1]\n      end\n    ```\n  - **dataset-method-based** (`setting validation`) ([documentation](#method-based-validation))\n    ```ruby\n      validate 'db.user', by: :check_user, strict: true\n\n      def check_user(value)\n        value.is_a?(String)\n      end\n    ```\n  - **dataset-method-based** (`dataset validation`) ([documentation](#method-based-validation))\n    ```ruby\n      validate by: :check_config, strict: false\n\n      def check_config\n        settings.user == User[1]\n      end\n    ```\n- provides a **set of standard validations** ([documentation](#predefined-validations)):\n  - DSL: `validate 'key.pattern', :predefned_validator`;\n  - supports `strict` behavior;\n- you can define your own predefined validators (class-related and global-related) ([documentation](#custom-predefined-validators));\n\n---\n\n### Key search pattern\n\n**Key search pattern** works according to the following rules:\n\n- works in `RabbitMQ`-like key pattern ruleses;\n- has a string format;\n- nested configs are defined by a set of keys separated by `.`-symbol;\n- if the setting key name at the current nesting level does not matter - use `*`;\n- if both the setting key name and nesting level does not matter - use `#`\n- examples:\n  - `db.settings.user` - matches to `db.settings.user` setting;\n  - `db.settings.*` - matches to all setting keys inside `db.settings` group of settings;\n  - `db.*.user` - matches to all `user` setting keys at the first level of `db` group of settings;\n  - `#.user` - matches to all `user` setting keys;\n  - `service.#.password` - matches to all `password` setting keys at all levels of `service` group of settings;\n  - `#` - matches to ALL setting keys;\n  - `*` - matches to all setting keys at the root level;\n  - and etc;\n\n---\n\n### Proc-based validation\n\n- your proc should return truthy value or falsy value;\n- `nil` values are ignored by default;\n- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);\n- how to validate setting keys:\n  - define proc with attribute: `validate 'your.setting.path' do |value|; end`\n  - proc will receive setting value;\n- how to validate dataset instance:\n  - define proc without setting key pattern: `validate do; end`;\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :db do\n    setting :user, 'D@iVeR'\n    setting :password, 'test123'\n  end\n\n  setting :service do\n    setting :address, 'google.ru'\n    setting :protocol, 'https'\n\n    setting :creds do\n      seting :admin, 'D@iVeR'\n    end\n  end\n\n  setting :enabled, false\n  setting :token, '1a2a3a', strict: true\n\n  # validates:\n  #   - db.password\n  validate 'db.password' do |value|\n    value.is_a?(String)\n  end\n\n  # validates:\n  #   - service.address\n  #   - service.protocol\n  #   - service.creds.user\n  validate 'service.#' do |value|\n    value.is_a?(String)\n  end\n\n  # validates:\n  #   - dataset instance\n  validate do # NOTE: no setting key pattern\n    settings.enabled == false\n  end\n\n  # do not ignore `nil` (strict: true)\n  validate(:token, strict: true) do\n    value.is_a?(String)\n  end\nend\n\nconfig = Config.new\nconfig.settings.db.password = 123 # =\u003e Qonfig::ValidationError (should be a string)\nconfig.settings.service.address = 123 # =\u003e Qonfig::ValidationError (should be a string)\nconfig.settings.service.protocol = :http # =\u003e Qonfig::ValidationError (should be a string)\nconfig.settings.service.creds.admin = :billikota # =\u003e Qonfig::ValidationError (should be a string)\nconfig.settings.enabled = true # =\u003e Qonfig::ValidationError (isnt `true`)\n\nconfig.settings.db.password = nil # ok, nil is ignored (non-strict behavior)\nconfig.settings.token = nil # =\u003e Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of string)\n```\n\n---\n\n### Method-based validation\n\n- method should return truthy value or falsy value;\n- `nil` values are ignored by default;\n- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);\n- how to validate setting keys:\n  - define validation: `validate 'db.*.user', by: :your_custom_method`;\n  - define your method with attribute: `def your_custom_method(setting_value); end`\n- how to validate config instance\n  - define validation: `validate by: :your_custom_method`\n  - define your method without attributes: `def your_custom_method; end`\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :services do\n    setting :counts do\n      setting :google, 2\n      setting :rambler, 3\n    end\n\n    setting :minimals do\n      setting :google, 1\n      setting :rambler, 0\n    end\n  end\n\n  setting :enabled, true\n  setting :timeout, 12345, strict: true\n\n  # validates:\n  #   - services.counts.google\n  #   - services.counts.rambler\n  #   - services.minimals.google\n  #   - services.minimals.rambler\n  validate 'services.#', by: :check_presence\n\n  # validates:\n  #   - dataset instance\n  validate by: :check_state # NOTE: no setting key pattern\n\n  # do not ignore `nil` (strict: true)\n  validate :timeout, strict: true, by: :check_timeout\n\n  def check_presence(value)\n    value.is_a?(Numeric) \u0026\u0026 value \u003e 0\n  end\n\n  def check_state\n    settings.enabled.is_a?(TrueClass) || settings.enabled.is_a?(FalseClass)\n  end\n\n  def check_timeout(value)\n    value.is_a?(Numeric)\n  end\nend\n\nconfig = Config.new\n\nconfig.settings.counts.google = 0 # =\u003e Qonfig::ValidationError (\u003c 0)\nconfig.settings.minimals.google = -1 # =\u003e Qonfig::ValidationError (\u003c 0)\nconfig.settings.minimals.rambler = 'no' # =\u003e Qonfig::ValidationError (should be a numeric)\n\nconfig.settings.counts.rambler = nil # ok, nil is ignored (default non-strict behavior)\nconfig.settings.enabled = nil # ok, nil is ignored (default non-strict behavior)\nconfig.settings.timeout = nil # =\u003e Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of numeric)\n```\n\n---\n\n### Predefined validations\n\n- DSL: `validate 'key.pattern', :predefned_validator`\n- `nil` values are ignored by default;\n- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);\n- predefined validators:\n  - `:not_nil`\n  - `:integer`\n  - `:float`\n  - `:numeric`\n  - `:big_decimal`\n  - `:array`\n  - `:hash`\n  - `:string`\n  - `:symbol`\n  - `:text` (`string` or `symbol`)\n  - `:boolean`\n  - `:class`\n  - `:module`\n  - `:proc`\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :user, 'empty'\n  setting :password, 'empty'\n\n  setting :service do\n    setting :provider, :empty\n    setting :protocol, :empty\n    setting :on_fail, -\u003e { puts 'atata!' }\n  end\n\n  setting :ignorance, false\n\n  validate 'user', :string\n  validate 'password', :string\n  validate 'service.provider', :text\n  validate 'service.protocol', :text\n  validate 'service.on_fail', :proc\n  validate 'ignorance', :not_nil\nend\n\nconfig = Config.new do |conf|\n  conf.user = 'D@iVeR'\n  conf.password = 'test123'\n  conf.service.provider = :google\n  conf.service.protocol = :https\nend # NOTE: all right :)\n\nconfig.settings.ignorance = nil # =\u003e Qonfig::ValidationError (cant be nil)\n```\n\n---\n\n### Custom predefined validators\n\n- DSL: `.define_validator(name, \u0026validation) { |value| ... }` - create your own predefined validator;\n- **class-level**: define validators related only to the concrete config class;\n- **global-level**: define validators related to all config classes (`Qonfig::DataSet.define_validator`);\n- you can re-define any global and inherited validator (at class level);\n- you can re-define any already registered global validator on `Qonfig::DataSet` (at global-level);\n\n#### Define your own class-level validator\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  # NOTE: definition\n  define_validator(:user_type) { |value| value.is_a?(User) }\n\n  setting :admin # some key\n\n  # NOTE: usage\n  validate :admin, :user_type\nend\n```\n\n#### Define new global validator\n\n```ruby\nQonfig::DataSet.define_validator(:secured_value) do |value|\n  value == '***'\nend\n\nclass Config \u003c Qonfig::DataSet\n  setting :password\n  validate :password, :secured_value\nend\n```\n\n#### Re-definition of existing validators in child classes\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  # NOTE: redefine existing :text validator only in Config class\n  define_validator(:text) { |value| value.is_a?(String) }\n\n  # NOTE: some custom validator that can be redefined in child classes\n  define_validator(:user) { |value| value.is_a?(User) }\nend\n\nclass SubConfig \u003c Qonfig\n  define_validator(:user) { |value| value.is_a?(AdminUser) } # NOTE: redefine inherited :user validator\nend\n```\n\n#### Re-definition of existing global validators\n\n```ruby\n# NOTE: redefine already existing :numeric validator\nQonfig::DataSet.define_validator(:numeric) do |value|\n  value.is_a?(Numeric) || (value.is_a?(String) \u0026\u0026 value.match?(/\\A\\d+\\.*\\d+\\z/))\nend\n```\n\n---\n\n### Validation of potential setting values\n\n- (**instance-level**) `#valid_with?(setting_values = {}, \u0026configuration)` - check that current config instalce will be valid with passed configurations;\n- (**class-level**) `.valid_with?(setting_values = {}, \u0026configuration)` - check that potential config instancess will be valid with passed configurations;\n- makes no assignments;\n\n#### #valid_with? (instance-level)\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :enabled, false\n  setting :queue do\n    setting :adapter, 'sidekiq'\n  end\n\n  validate :enabled, :boolean\n  validate 'queue.adapter', :string\nend\n\nconfig = Config.new\n\nconfig.valid_with?(enabled: true, queue: { adapter: 'que' }) # =\u003e true\nconfig.valid_with?(enabled: 123) # =\u003e false (should be a type of boolean)\nconfig.valid_with?(enabled: true, queue: { adapter: Sidekiq }) # =\u003e false (queue.adapter should be a type of string)\n\n# do-config notation is supported too\nconfig.valid_with?(enabled: true) do |conf|\n  conf.queue.adapter = :sidekiq\nend\n# =\u003e false (queue.adapter should be a type of string)\n```\n\n#### .valid_with? (class-level)\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :enabled, false\n  setting :queue do\n    setting :adapter, 'sidekiq'\n  end\n\n  validate :enabled, :boolean\n  validate 'queue.adapter', :string\nend\n\nConfig.valid_with?(enabled: true, queue: { adapter: 'que' }) # =\u003e true\nConfig.valid_with?(enabled: 123) # =\u003e false (should be a type of boolean)\nConfig.valid_with?(enabled: true, queue: { adapter: Sidekiq }) # =\u003e false (queue.adapter should be a type of string)\n\n# do-config notation is supported too\nConfig.valid_with?(enabled: true) do |config|\n  config.queue.adapter = :sidekiq\nend\n# =\u003e false (queue.adapter should be a type of string)\n```\n\n---\n\n## Work with files\n\n- **Setting keys definition**\n  - [Load from YAML file](#load-from-yaml-file)\n  - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)\n  - [Load from JSON file](#load-from-json-file)\n  - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)\n  - [Load from ENV](#load-from-env)\n  - [Load from \\_\\_END\\_\\_](#load-from-__end__) (aka `load_from_self`)\n  - [Expose \\_\\_END\\_\\_](#expose-__end__) (aka `expose_self`)\n- **Setting values**\n  - [Default setting values file](#default-setting-values-file)\n  - [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)\n  - [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)\n  - [Load setting values from \\_\\_END\\_\\_](#load-setting-values-from-__end__-by-instance)\n  - [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)\n- **Daily work**\n  - [Save to JSON file](#save-to-json-file) (`save_to_json`)\n  - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)\n\n---\n\n### Load from YAML file\n\n- supports `ERB`;\n- `:strict` mode (fail behaviour when the required yaml file doesnt exist):\n  - `true` (by default) - causes `Qonfig::FileNotFoundError`;\n  - `false` - do nothing, ignore current command;\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n```yaml\n# travis.yml\n\nsudo: false\nlanguage: ruby\nrvm:\n  - ruby-head\n  - jruby-head\n```\n\n```yaml\n# project.yml\n\nenable_api: false\nSidekiq/Scheduler:\n  enable: true\n```\n\n```yaml\n# ruby_data.yml\n\nversion: \u003c%= RUBY_VERSION %\u003e\nplatform: \u003c%= RUBY_PLATFORM %\u003e\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :ruby do\n    load_from_yaml 'ruby_data.yml'\n  end\n\n  setting :travis do\n    load_from_yaml 'travis.yml'\n  end\n\n  load_from_yaml 'project.yml'\nend\n\nconfig = Config.new\n\nconfig.settings.travis.sudo # =\u003e false\nconfig.settings.travis.language # =\u003e 'ruby'\nconfig.settings.travis.rvm # =\u003e ['ruby-head', 'jruby-head']\nconfig.settings.enable_api # =\u003e false\nconfig.settings['Sidekiq/Scheduler']['enable'] #=\u003e true\nconfig.settings.ruby.version # =\u003e '2.5.1'\nconfig.settings.ruby.platform # =\u003e 'x86_64-darwin17'\n```\n\n```ruby\n# --- strict mode ---\nclass Config \u003c Qonfig::DataSet\n  setting :nonexistent_yaml do\n    load_from_yaml 'nonexistent_yaml.yml', strict: true # true by default\n  end\n\n  setting :another_key\nend\n\nConfig.new # =\u003e Qonfig::FileNotFoundError\n\n# --- non-strict mode ---\nclass Config \u003c Qonfig::DataSet\n  settings :nonexistent_yaml do\n    load_from_yaml 'nonexistent_yaml.yml', strict: false\n  end\n\n  setting :another_key\nend\n\nConfig.new.to_h # =\u003e { \"nonexistent_yaml\" =\u003e {}, \"another_key\" =\u003e nil }\n```\n\n---\n\n### Expose YAML\n\n- load configurations from YAML file in Rails-like manner (with environments);\n- works in `load_from_yaml` manner;\n- `via:` - how an environment will be determined:\n    - `:file_name`\n        - load configuration from YAML file that have an `:env` part in it's name;\n    - `:env_key`\n        - load configuration from YAML file;\n        - concrete configuration should be defined in the root key with `:env` name;\n- `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);\n- `strict:` - requires the existence of the file and/or key with the name of the used environment:\n    - `true`:\n        - file should exist;\n        - root key with `:env` name should exist (if `via: :env_key` is used);\n        - raises `Qonfig::ExposeError` if file does not contain the required env key (if `via: :env` key is used);\n        - raises `Qonfig::FileNotFoundError` if the required file does not exist;\n    - `false`:\n        - file is not required;\n        - root key with `:env` name is not required (if `via: :env_key` is used);\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n#### Environment is defined as a root key of YAML file\n\n```yaml\n# config/project.yml\n\ndefault: \u0026default\n  enable_api_mode: true\n  google_key: 12345\n  window:\n    width: 100\n    height: 100\n\ndevelopment:\n  \u003c\u003c: *default\n\ntest:\n  \u003c\u003c: *default\n  sidekiq_instrumentation: false\n\nstaging:\n  \u003c\u003c: *default\n  google_key: 777\n  enable_api_mode: false\n\nproduction:\n  google_key: asd1-39sd-55aI-O92x\n  enable_api_mode: true\n  window:\n    width: 50\n    height: 150\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  expose_yaml 'config/project.yml', via: :env_key, env: :production # load from production env\n\n  # NOTE: in rails-like application you can use this:\n  expose_yaml 'config/project.yml', via: :env_key, env: Rails.env\nend\n\nconfig = Config.new\n\nconfig.settings.enable_api_mode # =\u003e true (from :production subset of keys)\nconfig.settings.google_key # =\u003e asd1-39sd-55aI-O92x (from :production subset of keys)\nconfig.settings.window.width # =\u003e 50 (from :production subset of keys)\nconfig.settings.window.height # =\u003e 150 (from :production subset of keys)\n```\n\n#### Environment is defined as a part of YAML file name\n\n```yaml\n# config/sidekiq.staging.yml\n\nweb:\n  username: staging_admin\n  password: staging_password\n```\n\n```yaml\n# config/sidekiq.production.yml\n\nweb:\n  username: urj1o2\n  password: u192jd0ixz0\n```\n\n```ruby\nclass SidekiqConfig \u003c Qonfig::DataSet\n  # NOTE: file name should be described WITHOUT environment part (in file name attribute)\n  expose_yaml 'config/sidekiq.yml', via: :file_name, env: :staging # load from staging env\n\n  # NOTE: in rails-like application you can use this:\n  expose_yaml 'config/sidekiq.yml', via: :file_name, env: Rails.env\nend\n\nconfig = SidekiqConfig.new\n\nconfig.settings.web.username # =\u003e staging_admin (from sidekiq.staging.yml)\nconfig.settings.web.password # =\u003e staging_password (from sidekiq.staging.yml)\n```\n\n---\n\n### Load from JSON file\n\n- supports `ERB`;\n- `:strict` mode (fail behaviour when the required yaml file doesnt exist):\n  - `true` (by default) - causes `Qonfig::FileNotFoundError`;\n  - `false` - do nothing, ignore current command;\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n```json\n// options.json\n\n{\n  \"user\": \"0exp\",\n  \"password\": 12345,\n  \"rubySettings\": {\n    \"allowedVersions\": [\"2.3\", \"2.4.2\", \"1.9.8\"],\n    \"gitLink\": null,\n    \"withAdditionals\": false\n  }\n}\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  load_from_json 'options.json'\nend\n\nconfig = Config.new\n\nconfig.settings.user # =\u003e '0exp'\nconfig.settings.password # =\u003e 12345\nconfig.settings.rubySettings.allowedVersions # =\u003e ['2.3', '2.4.2', '1.9.8']\nconfig.settings.rubySettings.gitLink # =\u003e nil\nconfig.settings.rubySettings.withAdditionals # =\u003e false\n```\n\n```ruby\n# --- strict mode ---\nclass Config \u003c Qonfig::DataSet\n  setting :nonexistent_json do\n    load_from_json 'nonexistent_json.json', strict: true # true by default\n  end\n\n  setting :another_key\nend\n\nConfig.new # =\u003e Qonfig::FileNotFoundError\n\n# --- non-strict mode ---\nclass Config \u003c Qonfig::DataSet\n  settings :nonexistent_json do\n    load_from_json 'nonexistent_json.json', strict: false\n  end\n\n  setting :another_key\nend\n\nConfig.new.to_h # =\u003e { \"nonexistent_json\" =\u003e {}, \"another_key\" =\u003e nil }\n```\n\n---\n\n### Expose JSON\n\n- load configurations from JSON file in Rails-like manner (with environments);\n- works in `load_from_jsom`/`expose_yaml` manner;\n- `via:` - how an environment will be determined:\n    - `:file_name`\n        - load configuration from JSON file that have an `:env` part in it's name;\n    - `:env_key`\n        - load configuration from JSON file;\n        - concrete configuration should be defined in the root key with `:env` name;\n- `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);\n- `strict:` - requires the existence of the file and/or key with the name of the used environment:\n    - `true`:\n        - file should exist;\n        - root key with `:env` name should exist (if `via: :env_key` is used);\n        - raises `Qonfig::ExposeError` if file does not contain the required env key (if `via: :env` key is used);\n        - raises `Qonfig::FileNotFoundError` if the required file does not exist;\n    - `false`:\n        - file is not required;\n        - root key with `:env` name is not required (if `via: :env_key` is used);\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n#### Environment is defined as a root key of JSON file\n\n```json\n// config/project.json\n\n{\n  \"development\": {\n    \"api_mode_enabled\": true,\n    \"logging\": false,\n    \"db_driver\": \"sequel\",\n    \"throttle_requests\": false,\n    \"credentials\": {}\n  },\n  \"test\": {\n    \"api_mode_enabled\": true,\n    \"logging\": false,\n    \"db_driver\": \"in_memory\",\n    \"throttle_requests\": false,\n    \"credentials\": {}\n  },\n  \"staging\": {\n    \"api_mode_enabled\": true,\n    \"logging\": true,\n    \"db_driver\": \"active_record\",\n    \"throttle_requests\": true,\n    \"credentials\": {}\n  },\n  \"production\": {\n    \"api_mode_enabled\": true,\n    \"logging\": true,\n    \"db_driver\": \"rom\",\n    \"throttle_requests\": true,\n    \"credentials\": {}\n  }\n}\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  expose_json 'config/project.json', via: :env_key, env: :production # load from production env\n\n  # NOTE: in rails-like application you can use this:\n  expose_json 'config/project.json', via: :env_key, env: Rails.env\nend\n\nconfig = Config.new\n\nconfig.settings.api_mode_enabled # =\u003e true (from :production subset of keys)\nconfig.settings.logging # =\u003e true (from :production subset of keys)\nconfig.settings.db_driver # =\u003e \"rom\" (from :production subset of keys)\nconfig.settings.throttle_requests # =\u003e true (from :production subset of keys)\nconfig.settings.credentials # =\u003e {} (from :production subset of keys)\n```\n\n#### Environment is defined as a part of JSON file name\n\n```json\n// config/sidekiq.staging.json\n{\n  \"web\": {\n    \"username\": \"staging_admin\",\n    \"password\": \"staging_password\"\n  }\n}\n```\n\n```json\n// config/sidekiq.production.json\n{\n  \"web\": {\n    \"username\": \"urj1o2\",\n    \"password\": \"u192jd0ixz0\"\n  }\n}\n```\n\n```ruby\nclass SidekiqConfig \u003c Qonfig::DataSet\n  # NOTE: file name should be described WITHOUT environment part (in file name attribute)\n  expose_json 'config/sidekiq.json', via: :file_name, env: :staging # load from staging env\n\n  # NOTE: in rails-like application you can use this:\n  expose_json 'config/sidekiq.json', via: :file_name, env: Rails.env\nend\n\nconfig = SidekiqConfig.new\n\nconfig.settings.web.username # =\u003e \"staging_admin\" (from sidekiq.staging.json)\nconfig.settings.web.password # =\u003e \"staging_password\" (from sidekiq.staging.json)\n```\n\n---\n\n### Load from ENV\n\n- `:convert_values` (`false` by default):\n  - `'t'`, `'T'`, `'true'`, `'TRUE'` - covnerts to `true`;\n  - `'f'`, `'F'`, `'false'`, `'FALSE'` - covnerts to `false`;\n  - `1`, `23` and etc - converts to `Integer`;\n  - `1.25`, `0.26` and etc - converts to `Float`;\n  - `1, 2, test`, `FALSE,Qonfig` (strings without quotes that contains at least one comma) -\n    converts to `Array` with recursively converted values;\n  - `'\"please, test\"'`, `\"'test, please'\"` (quoted strings) - converts to `String` without quotes;\n- `:prefix` - load ENV variables which names starts with a prefix:\n  - `nil` (by default) - empty prefix;\n  - `Regexp` - names that match the regexp pattern;\n  - `String` - names which starts with a passed string;\n- `:trim_prefix` (`false` by default);\n\n```ruby\n# some env variables\nENV['QONFIG_BOOLEAN'] = 'true'\nENV['QONFIG_INTEGER'] = '0'\nENV['QONFIG_STRING'] = 'none'\nENV['QONFIG_ARRAY'] = '1, 2.5, t, f, TEST'\nENV['QONFIG_MESSAGE'] = '\"Hello, Qonfig!\"'\nENV['RUN_CI'] = '1'\n\nclass Config \u003c Qonfig::DataSet\n  # nested\n  setting :qonfig do\n    load_from_env convert_values: true, prefix: 'QONFIG' # or /\\Aqonfig.*\\z/i\n  end\n\n  setting :trimmed do\n    load_from_env convert_values: true, prefix: 'QONFIG_', trim_prefix: true # trim prefix\n  end\n\n  # on the root\n  load_from_env\nend\n\nconfig = Config.new\n\n# customized\nconfig.settings['qonfig']['QONFIG_BOOLEAN'] # =\u003e true ('true' =\u003e true)\nconfig.settings['qonfig']['QONFIG_INTEGER'] # =\u003e 0 ('0' =\u003e 0)\nconfig.settings['qonfig']['QONFIG_STRING'] # =\u003e 'none'\nconfig.settings['qonfig']['QONFIG_ARRAY'] # =\u003e [1, 2.5, true, false, 'TEST']\nconfig.settings['qonfig']['QONFIG_MESSAGE'] # =\u003e 'Hello, Qonfig!'\nconfig.settings['qonfig']['RUN_CI'] # =\u003e Qonfig::UnknownSettingError\n\n# trimmed (and customized)\nconfig.settings['trimmed']['BOOLEAN'] # =\u003e true ('true' =\u003e true)\nconfig.settings['trimmed']['INTEGER'] # =\u003e 0 ('0' =\u003e 0)\nconfig.settings['trimmed']['STRING'] # =\u003e 'none'\nconfig.settings['trimmed']['ARRAY'] # =\u003e [1, 2.5, true, false, 'TEST']\nconfig.settings['trimmed']['MESSAGE'] # =\u003e 'Hello, Qonfig!'\nconfig.settings['trimmed']['RUN_CI'] # =\u003e Qonfig::UnknownSettingError\n\n# default\nconfig.settings['QONFIG_BOOLEAN'] # =\u003e 'true'\nconfig.settings['QONFIG_INTEGER'] # =\u003e '0'\nconfig.settings['QONFIG_STRING'] # =\u003e 'none'\nconfig.settings['QONFIG_ARRAY'] # =\u003e '1, 2.5, t, f, TEST'\nconfig.settings['QONFIG_MESSAGE'] # =\u003e '\"Hello, Qonfig!\"'\nconfig.settings['RUN_CI'] # =\u003e '1'\n```\n\n---\n\n### Load from \\_\\_END\\_\\_\n\n- aka `load_from_self`\n- `:format` - specify the format of data placed under the `__END__` instruction:\n  - `format: :dynamic` (default) - automatic format resolvation;\n  - `format: :yaml` - **YAML** format;\n  - `format: :json` - **JSON** format;\n  - `format: :toml` - **TOML** format (via `toml`-plugin);\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  load_from_self # on the root (:dynamic format is used by default)\n\n  setting :nested do\n    load_from_self, format: :yaml # with explicitly identified YAML format\n  end\nend\n\nconfig = Config.new\n\n# on the root\nconfig.settings.ruby_version # =\u003e '2.5.1'\nconfig.settings.secret_key # =\u003e 'top-mega-secret'\nconfig.settings.api_host # =\u003e 'super.puper-google.com'\nconfig.settings.connection_timeout.seconds # =\u003e 10\nconfig.settings.connection_timeout.enabled # =\u003e false\n\n# nested\nconfig.settings.nested.ruby_version # =\u003e '2.5.1'\nconfig.settings.nested.secret_key # =\u003e 'top-mega-secret'\nconfig.settings.nested.api_host # =\u003e 'super.puper-google.com'\nconfig.settings.nested.connection_timeout.seconds # =\u003e 10\nconfig.settings.nested.connection_timeout.enabled # =\u003e false\n\n__END__\n\nruby_version: \u003c%= RUBY_VERSION %\u003e\nsecret_key: top-mega-secret\napi_host: super.puper-google.com\nconnection_timeout:\n  seconds: 10\n  enabled: false\n```\n\n---\n\n### Expose \\_\\_END\\_\\_\n\n- aka `expose_self`;\n- works in `expose_json` and `expose_yaml` manner, but with `__END__` instruction of the current file;\n- `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);\n- `:format` - specify the format of data placed under the `__END__` instruction:\n  - `format: :dynamic` (default) - automatic format resolvation;\n  - `format: :yaml` - **YAML** format;\n  - `format: :json` - **JSON** format;\n  - `format: :toml` - **TOML** format (via `toml`-plugin);\n- `:replace_on_merge` - whether the setting should be replaced on the key conflict, otherwise, it will be deep merged (default);\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  expose_self env: :production, format: :yaml # with explicitly identified YAML format\n\n  # NOTE: for Rails-like applications you can use this:\n  expose_self env: Rails.env\nend\n\nconfig = Config.new\n\nconfig.settings.log # =\u003e true (from :production environment)\nconfig.settings.api_enabled # =\u003e true (from :production environment)\nconfig.settings.creds.user # =\u003e \"D@iVeR\" (from :production environment)\nconfig.settings.creds.password # =\u003e \"test123\" (from :production environment)\n\n__END__\n\ndefault: \u0026default\n  log: false\n  api_enabled: true\n  creds:\n    user: admin\n    password: 1234\n\ndevelopment:\n  \u003c\u003c: *default\n  log: true\n\ntest:\n  \u003c\u003c: *default\n  log: false\n\nstaging:\n  \u003c\u003c: *default\n\nproduction:\n  \u003c\u003c: *default\n  log: true\n  creds:\n    user: D@iVeR\n    password: test123\n```\n\n---\n\n### Default setting values file\n\n- defines a file that should be used for setting values initialization for your config object;\n- `.values_file(file_path, format: :dynamic, strict: false, expose: nil)`\n  - `file_path` - full file path or `:self` (`:self` menas \"load setting values from __END__ data\");\n  - `:format` - defines the format of file (`:dynamic` means \"try to automatically infer the file format\") (`:dynamic` by default);\n    - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);\n  - `:strict` - rerquires that file (or __END__-data) should exist (`false` by default);\n  - `:expose` - what the environment-based subset of keys should be used (`nil` means \"do not use any subset of keys\") (`nil` by default);\n- extra keys that does not exist in your config will cause an exception `Qonfig::SettingNotFound` respectively;\n- initial values will be rewritten by values defined in your file;\n\n#### Default behavior\n\n```yaml\n# sidekiq.yml\n\nadapter: sidekiq\noptions:\n  processes: 10\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  values_file 'sidekiq.yml', format: :yaml\n\n  setting :adapter, 'que'\n  setting :options do\n    setting :processes, 2\n    setting :threads, 5\n    setting :protected, false\n  end\nend\n\nconfig = Config.new\n\nconfig.settings.adapter # =\u003e \"sidekiq\" (from sidekiq.yml)\nconfig.settings.options.processes # =\u003e 10 (from sidekiq.yml)\nconfig.settings.options.threads # =\u003e 5 (original value)\nconfig.settings.options.protected # =\u003e false (original value)\n```\n\n#### Load values from \\_\\_END\\_\\_-data\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  values_file :self, format: :yaml\n\n  setting :user\n  setting :password\n  setting :enabled, true\nend\n\nconfig = Config.new\n\nconfig.settings.user # =\u003e \"D@iVeR\" (from __END__ data)\nconfig.settings.password # =\u003e \"test123\" (from __END__ data)\nconfig.settings.enabled # =\u003e true (original value)\n\n__END__\n\nuser: 'D@iVeR'\npassword: 'test123'\n```\n\n#### Setting values with environment separation\n\n```yaml\n# sidekiq.yml\n\ndevelopment:\n  adapter: :in_memory\n  options:\n    threads: 10\n\nproduction:\n  adapter: :sidekiq\n  options:\n    threads: 150\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  values_file 'sidekiq.yml', format: :yaml, expose: :development\n\n  setting :adapter\n  setting :options do\n    setting :threads\n  end\nend\n\nconfig = Config.new\n\nconfig.settings.adapter # =\u003e 'in_memory' (development keys subset)\nconfig.settings.options.threads # =\u003e 10 (development keys subset)\n```\n\n#### File does not exist\n\n```ruby\n# non-strict behavior (default)\nclass Config \u003c Qonfig::DataSet\n  values_file 'sidekiq.yml'\nend\n\nconfig = Config.new # no error\n\n# strict behavior (strict: true)\nclass Config \u003c Qonfig::DataSet\n  values_file 'sidekiq.yml', strict: true\nend\n\nconfig = Config.new # =\u003e Qonfig::FileNotFoundError\n```\n\n---\n\n### Load setting values from YAML file (by instance)\n\n- prvoides an ability to load predefined setting values from a yaml file;\n- `#load_from_yaml(file_path, strict: true, expose: nil, \u0026configurations)`\n  - `file_path` - full file path or `:self` (`:self` means \"load setting values from __END__ data\");\n  - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);\n  - `:expose` - what the environment-based subset of keys should be used (`nil` means \"do not use any subset of keys\") (`nil` by default);\n  - `\u0026configurations` - `do |config|` ability :)\n\n#### Default behavior\n\n```yaml\n# config.yml\n\ndomain: google.ru\ncreds:\n  auth_token: test123\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  seting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token, 'test'\n  end\nend\n\nconfig = Config.new\nconfig.settings.domain # =\u003e \"test.com\"\nconfig.settings.creds.auth_token # =\u003e \"test\"\n\n# load new values\nconfig.load_from_yaml('config.yml')\n\nconfig.settings.domain # =\u003e \"google.ru\" (from config.yml)\nconfig.settings.creds.auth_token # =\u003e \"test123\" (from config.yml)\n```\n\n#### Load from \\_\\_END\\_\\_\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  seting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token, 'test'\n  end\nend\n\nconfig = Config.new\nconfig.settings.domain # =\u003e \"test.com\"\nconfig.settings.creds.auth_token # =\u003e \"test\"\n\n# load new values\nconfig.load_from_yaml(:self)\nconfig.settings.domain # =\u003e \"yandex.ru\" (from __END__-data)\nconfig.settings.creds.auth_token # =\u003e \"CK0sIdA\" (from __END__-data)\n\n__END__\n\ndomain: yandex.ru\ncreds:\n  auth_token: CK0sIdA\n```\n\n#### Setting values with environment separation\n\n```yaml\n# config.yml\n\ndevelopment:\n  domain: dev.google.ru\n  creds:\n    auth_token: kekpek\n\nproduction:\n  domain: google.ru\n  creds:\n    auth_token: Asod1\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token\n  end\nend\n\nconfig = Config.new\n\n# load new values (expose development settings)\nconfig.load_from_yaml('config.yml', expose: :development)\n\nconfig.settings.domain # =\u003e \"dev.google.ru\" (from config.yml)\nconfig.settings.creds.auth_token # =\u003e \"kek.pek\" (from config.yml)\n```\n\n---\n\n### Load setting values from JSON file (by instance)\n\n- prvoides an ability to load predefined setting values from a json file;\n- `#load_from_json(file_path, strict: true, expose: nil, \u0026configurations)`\n  - `file_path` - full file path or `:self` (`:self` means \"load setting values from __END__ data\");\n  - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);\n  - `:expose` - what the environment-based subset of keys should be used (`nil` means \"do not use any subset of keys\") (`nil` by default);\n  - `\u0026configurations` - `do |config|` ability :)\n\n#### Default behavior\n\n```json\n// config.json\n\n{\n  \"domain\": \"google.ru\",\n  \"creds\": {\n    \"auth_token\": \"test123\"\n  }\n}\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  seting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token, 'test'\n  end\nend\n\nconfig = Config.new\nconfig.settings.domain # =\u003e \"test.com\"\nconfig.settings.creds.auth_token # =\u003e \"test\"\n\n# load new values\nconfig.load_from_json('config.json')\n\nconfig.settings.domain # =\u003e \"google.ru\" (from config.json)\nconfig.settings.creds.auth_token # =\u003e \"test123\" (from config.json)\n```\n\n#### Load from \\_\\_END\\_\\_\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  seting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token, 'test'\n  end\nend\n\nconfig = Config.new\nconfig.settings.domain # =\u003e \"test.com\"\nconfig.settings.creds.auth_token # =\u003e \"test\"\n\n# load new values\nconfig.load_from_json(:self)\nconfig.settings.domain # =\u003e \"yandex.ru\" (from __END__-data)\nconfig.settings.creds.auth_token # =\u003e \"CK0sIdA\" (from __END__-data)\n\n__END__\n\n{\n  \"domain\": \"yandex.ru\",\n  \"creds\": {\n    \"auth_token\": \"CK0sIdA\"\n  }\n}\n```\n\n#### Setting values with environment separation\n\n```json\n// config.json\n\n{\n  \"development\": {\n    \"domain\": \"dev.google.ru\",\n    \"creds\": {\n      \"auth_token\": \"kekpek\"\n    }\n  },\n  \"production\": {\n    \"domain\": \"google.ru\",\n    \"creds\": {\n      \"auth_token\": \"Asod1\"\n    }\n  }\n}\n```\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :domain, 'test.com'\n  setting :creds do\n    setting :auth_token\n  end\nend\n\nconfig = Config.new\n\n# load new values (from development subset)\nconfig.load_from_json('config.json', expose: :development)\n\nconfig.settings.domain # =\u003e \"dev.google.ru\" (from config.json)\nconfig.settings.creds.auth_token # =\u003e \"kek.pek\" (from config.json)\n```\n---\n\n### Load setting values from \\_\\_END\\_\\_ (by instance)\n\n- prvoides an ability to load predefined setting values from `__END__` file section;\n- `#load_from_self(strict: true, expose: nil, \u0026configurations)`\n  - `:format` - defines the format of file (`:dynamic` means \"try to automatically infer the file format\") (`:dynamic` by default);\n    - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);\n  - `:strict` - requires that __END__-data should exist (`true` by default);\n  - `:expose` - what the environment-based subset of keys should be used (`nil` means \"do not use any subset of keys\") (`nil` by default);\n  - `\u0026configurations` - `do |config|` ability :)\n\n#### Default behavior\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :account, 'test'\n  setting :options do\n    setting :login, '0exp'\n    setting :password, 'test123'\n  end\nend\n\nconfig = Config.new\nconfig.settings.account # =\u003e \"test\" (original value)\nconfig.settings.options.login # =\u003e \"0exp\" (original value)\nconfig.settings.options.password # =\u003e \"test123\" (original value)\n\n# load new values\nconfig.load_from_self(format: :yaml)\n# or config.load_from_self\n\nconfig.settings.account # =\u003e \"real\" (from __END__-data)\nconfig.settings.options.login # =\u003e \"D@iVeR\" (from __END__-data)\nconfig.settings.options.password # =\u003e \"azaza123\" (from __END__-data)\n\n__END__\n\naccount: real\noptions:\n  login: D@iVeR\n  password: azaza123\n```\n\n#### Setting values with envvironment separation\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :domain, 'test.google.ru'\n  setting :options do\n    setting :login, 'test'\n    setting :password, 'test123'\n  end\nend\n\nconfig = Config.new\nconfig.settings.domain # =\u003e \"test.google.ru\" (original value)\nconfig.settings.options.login # =\u003e \"test\" (original value)\nconfig.settings.options.password # =\u003e \"test123\" (original value)\n\n# load new values\nconfig.load_from_self(format: :json, expose: :production)\n# or config.load_from_self(expose: production)\n\nconfig.settings.domain # =\u003e \"prod.google.ru\" (from __END__-data)\nconfig.settings.options.login # =\u003e \"prod\" (from __END__-data)\nconfig.settings.options.password # =\u003e \"prod123\" (from __END__-data)\n\n__END__\n\n{\n  \"development\": {\n    \"domain\": \"dev.google.ru\",\n    \"options\": {\n      \"login\": \"dev\",\n      \"password\": \"dev123\"\n    }\n  },\n  \"production\": {\n    \"domain\": \"prod.google.ru\",\n    \"options\": {\n      \"login\": \"prod\",\n      \"password\": \"prod123\"\n    }\n  }\n}\n```\n\n---\n\n### Load setting values from file manually (by instance)\n\n- prvoides an ability to load predefined setting values from a file;\n- works in instance-based `#load_from_yaml` / `#load_from_json` / `#load_from_self` manner;\n- signature: `#load_from_file(file_path, format: :dynamic, strict: true, expose: nil, \u0026configurations)`:\n  - `file_path` - full file path or `:self` (`:self` means \"load setting values from __END__ data\");\n  - `:format` - defines the format of file (`:dynamic` means \"try to automatically infer the file format\") (`:dynamic` by default);\n    - supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);\n  - `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);\n  - `:expose` - what the environment-based subset of keys should be used (`nil` means \"do not use any subset of keys\") (`nil` by default);\n  - `\u0026configurations` - `do |config|` ability :)\n- 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));\n\n---\n\n### Save to JSON file\n\n- `#save_to_json` - represents config object as a json structure and saves it to a file:\n  - uses native `::JSON.generate` under the hood;\n  - writes new file (or rewrites existing file);\n  - attributes:\n    - `:path` - (required) - file path;\n    - `:options` - (optional) - native `::JSON.generate` options (from stdlib):\n      - `:indent` - `\" \"` by default;\n      - `:space` - `\" \"` by default/\n      - `:object_nl` - `\"\\n\"` by default;\n    - `\u0026value_preprocessor` - (optional) - value pre-processor;\n\n#### Without value preprocessing (standard usage)\n\n```ruby\nclass AppConfig \u003c Qonfig::DataSet\n  setting :server do\n    setting :address, 'localhost'\n    setting :port, 12_345\n  end\n\n  setting :enabled, true\nend\n\nconfig = AppConfig.new\n\n# NOTE: save to json file\nconfig.save_to_json(path: 'config.json')\n```\n\n```json\n{\n \"sentry\": {\n  \"address\": \"localhost\",\n  \"port\": 12345\n },\n \"enabled\": true\n}\n```\n\n#### With value preprocessing and custom options\n\n```ruby\nclass AppConfig \u003c Qonfig::DataSet\n  setting :server do\n    setting :address, 'localhost'\n    setting :port, 12_345\n  end\n\n  setting :enabled, true\n  setting :dynamic, -\u003e { 1 + 2 }\nend\n\nconfig = AppConfig.new\n\n# NOTE: save to json file with custom options (no spaces / no new line / no indent; call procs)\nconfig.save_to_json(path: 'config.json', options: { indent: '', space: '', object_nl: '' }) do |value|\n  value.is_a?(Proc) ? value.call : value\nend\n```\n\n```json\n// no spaces / no new line / no indent / calculated \"dynamic\" setting key\n{\"sentry\":{\"address\":\"localhost\",\"port\":12345},\"enabled\":true,\"dynamic\":3}\n```\n\n---\n\n### Save to YAML file\n\n- `#save_to_yaml` - represents config object as a yaml structure and saves it to a file:\n  - uses native `::Psych.dump` under the hood;\n  - writes new file (or rewrites existing file);\n  - attributes:\n    - `:path` - (required) - file path;\n    - `:options` - (optional) - native `::Psych.dump` options (from stdlib):\n      - `:indentation` - `2` by default;\n      - `:line_width` - `-1` by default;\n      - `:canonical` - `false` by default;\n      - `:header` - `false` by default;\n      - `:symbolize_keys` - (non-native option) - `false` by default;\n    - `\u0026value_preprocessor` - (optional) - value pre-processor;\n\n#### Without value preprocessing (standard usage)\n\n```ruby\nclass AppConfig \u003c Qonfig::DataSet\n  setting :server do\n    setting :address, 'localhost'\n    setting :port, 12_345\n  end\n\n  setting :enabled, true\nend\n\nconfig = AppConfig.new\n\n# NOTE: save to yaml file\nconfig.save_to_yaml(path: 'config.yml')\n```\n\n```yaml\n---\nserver:\n  address: localhost\n  port: 12345\nenabled: true\n```\n\n#### With value preprocessing and custom options\n\n```ruby\nclass AppConfig \u003c Qonfig::DataSet\n  setting :server do\n    setting :address, 'localhost'\n    setting :port, 12_345\n  end\n\n  setting :enabled, true\n  setting :dynamic, -\u003e { 5 + 5 }\nend\n\nconfig = AppConfig.new\n\n# NOTE: save to yaml file with custom options (add yaml version header; call procs)\nconfig.save_to_yaml(path: 'config.yml', options: { header: true }) do |value|\n  value.is_a?(Proc) ? value.call : value\nend\n```\n\n```yaml\n# yaml version header / calculated \"dynamic\" setting key\n%YAML 1.1\n---\nserver:\n  address: localhost\n  port: 12345\nenabled: true\ndynamic: 10\n```\n\n---\n\n### Plugins\n\n- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`);\n- [pretty_print](#plugins-pretty_print) (beautified/prettified console output);\n- [vault](#plugins-vault) (provides `load_from_vault`, `expose_vault`)\n\n---\n\n#### Usage\n\n- show available plugins:\n\n```ruby\nQonfig.plugins # =\u003e [\"pretty_print\", \"toml\", ..., ...]\n```\n\n- load specific plugin:\n\n```ruby\nQonfig.plugin(:pretty_print) # or Qonfig.plugin('pretty_print')\n# -- or --\nQonfig.enable(:pretty_print) # or Qonfig.enable('pretty_print')\n# -- or --\nQonfig.load(:pretty_print) # or Qonfig.load('pretty_print')\n```\n\n- show loaded plugins:\n\n```ruby\nQonfig.loaded_plugins # =\u003e [\"pretty_print\"]\n# -- or --\nQonfig.enabled_plugins # =\u003e [\"pretty_print\"]\n```\n\n---\n\n### Plugins: toml\n\n- `Qonfig.plugin(:toml)`\n- adds support for `toml` format ([specification](https://github.com/toml-lang/toml));\n- depends on `toml-rb` gem ([link](https://github.com/emancu/toml-rb)) (tested on `\u003e= 2.0`);\n- supports TOML `0.5.0` format (dependency lock) (`toml-rb \u003e= 2.0`);\n- provides `.load_from_toml` (works in `.load_from_yaml` manner ([doc](#load-from-yaml-file)));\n- provides `.expose_toml` (works in `.expose_yaml` manner ([doc](#expose-yaml)));\n- provides `#save_to_toml` (works in `#save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);\n- provides `format: :toml` for `.values_file` ([doc]());\n- provides `#load_from_toml` (work in `#load_from_yaml` manner ([doc](#load-setting-values-from-yaml)));\n\n```ruby\n# 1) require external dependency\nrequire 'toml-rb'\n\n# 2) enable plugin\nQonfig.plugin(:toml)\n\n# 3) use toml :)\n```\n\n---\n\n### Plugins: pretty_print\n\n- `Qonfig.plugin(:pretty_print)`\n- gives you really comfortable and beautiful console output;\n- represents all setting keys in dot-notation format;\n\n#### Example:\n\n```ruby\nclass Config \u003c Qonfig::DataSet\n  setting :api do\n    setting :domain, 'google.ru'\n    setting :creds do\n      setting :account, 'D@iVeR'\n      setting :password, 'test123'\n    end\n  end\n\n  setting :log_requests, true\n  setting :use_proxy, true\nend\n\nconfig = Config.new\n```\n\n- before:\n\n```shell\n=\u003e #\u003cConfig:0x00007f9b6c01dab0\n @__lock__=\n  #\u003cQonfig::DataSet::Lock:0x00007f9b6c01da60\n   @access_lock=#\u003cThread::Mutex:0x00007f9b6c01da38\u003e,\n   @arbitary_lock=#\u003cThread::Mutex:0x00007f9b6c01d9e8\u003e,\n   @definition_lock=#\u003cThread::Mutex:0x00007f9b6c01da10\u003e\u003e,\n @settings=\n  #\u003cQonfig::Settings:0x00007f9b6c01d858\n   @__lock__=\n    #\u003cQonfig::Settings::Lock:0x00007f9b6c01d808\n     @access_lock=#\u003cThread::Mutex:0x00007f9b6c01d7b8\u003e,\n     @definition_lock=#\u003cThread::Mutex:0x00007f9b6c01d7e0\u003e,\n     @merge_lock=#\u003cThread::Mutex:0x00007f9b6c01d790\u003e\u003e,\n   @__mutation_callbacks__=\n    #\u003cQonfig::Settings::Callbacks:0x00007f9b6c01d8d0\n     @callbacks=[#\u003cProc:0x00007f9b6c01d8f8@/Users/daiver/Projects/qonfig/lib/qonfig/settings/builder.rb:39\u003e],\n     @lock=#\u003cThread::Mutex:0x00007f9b6c01d880\u003e\u003e,\n   @__options__=\n    {\"api\"=\u003e\n      #\u003cQonfig::Settings:0x00007f9b6c01d498\n# ... and etc\n```\n\n- after:\n\n```shell\n=\u003e #\u003cConfig:0x00007f9b6c01dab0\n api.domain: \"google.ru\",\n api.creds.account: \"D@iVeR\",\n api.creds.password: \"test123\",\n log_requests: true,\n use_proxy: true\u003e\n\n# -- or --\n\n=\u003e #\u003cConfig:0x00007f9b6c01dab0 api.domain: \"google.ru\", api.creds.account: \"D@iVeR\", api.creds.password: \"test123\", log_requests: true, use_proxy: true\u003e\n```\n\n---\n\n### Plugins: vault\n\n- `Qonfig.plugin(:vault)`\n- adds support for `vault kv store`, [more info](https://www.vaultproject.io/docs/secrets/kv/kv-v2)\n- depends on `vault` gem ([link](https://github.com/hashicorp/vault-ruby)) (tested on `\u003e= 0.1`);\n- provides `.load_from_vault` (works in `.load_from_yaml` manner ([doc](#load-from-yaml-file)));\n- provides `.expose_vault` (works in `.expose_yaml` manner ([doc](#expose-yaml)));\n\n```ruby\n# 1) require external dependency\nrequire 'vault'\n\n# 2) Setup vault client\n\nVault.address = 'http://localhost:8200'\nVault.token = 'super-duper-token-here'\n\n# 3) enable plugin\nQonfig.plugin(:vault)\n\n# 3) use vault :)\n```\n\n---\n\n## Roadmap\n\n- **General**:\n  - documentation rework;\n- **Major**:\n  - support for Rails-like secrets;\n  - support for persistent data storages (we want to store configs in multiple databases and files);\n  - rails plugin;\n  - support for pattern matching;\n  - support for type checking (via `rbs`, `typeprof`, `steep`);\n  - console utilities;\n- **Minor**:\n  - An ability to flag `Qonfig::Configurable`'s config object as `compacted` (`Qonfig::Compacted`);\n  - Instance-based behavior for `Vault` plugin, also use instance of `Vault` client instead of `Singleton`;\n  - External validation class with an importing api for better custom validations;\n  - Setting value changement trace (in `anyway_config` manner);\n  - Instantiation and reloading callbacks;\n  - File geneartors (.rb-files with a pre-filled code (and (maybe) with a pre-generated yaml/json/etc files));\n  - Setting value changement subscriptions and callbacks;\n  - CI rework (new configs, new steps, no strange dependencies);\n  - CI: thread-safety specs for `thruffleruby`;\n\n## Build\n\n```shell\nbin/rspec -w # test the core functionality and plugins\nbin/rspec -n # test only the core functionality\n```\n\n## Contributing\n\n- Fork it ( https://github.com/0exp/qonfig/fork )\n- Create your feature branch (`git checkout -b feature/my-new-feature`)\n- Commit your changes (`git commit -am '[my-new-featre] Add some feature'`)\n- Push to the branch (`git push origin feature/my-new-feature`)\n- Create new Pull Request\n\n## License\n\nReleased under MIT License.\n\n## Authors\n\n[Rustam Ibragimov](https://github.com/0exp)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fqonfig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0exp%2Fqonfig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fqonfig/lists"}