qonfig 0.0.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -2
  3. data/.jrubyrc +1 -0
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +15 -0
  6. data/.travis.yml +43 -4
  7. data/CHANGELOG.md +121 -0
  8. data/Gemfile +4 -2
  9. data/LICENSE.txt +1 -1
  10. data/README.md +1060 -19
  11. data/Rakefile +18 -4
  12. data/bin/console +5 -11
  13. data/bin/rspec +55 -0
  14. data/bin/setup +1 -0
  15. data/gemfiles/with_external_deps.gemfile +8 -0
  16. data/gemfiles/without_external_deps.gemfile +5 -0
  17. data/lib/qonfig.rb +22 -2
  18. data/lib/qonfig/command_set.rb +67 -0
  19. data/lib/qonfig/commands.rb +15 -0
  20. data/lib/qonfig/commands/add_nested_option.rb +45 -0
  21. data/lib/qonfig/commands/add_option.rb +41 -0
  22. data/lib/qonfig/commands/base.rb +12 -0
  23. data/lib/qonfig/commands/compose.rb +37 -0
  24. data/lib/qonfig/commands/expose_yaml.rb +159 -0
  25. data/lib/qonfig/commands/load_from_env.rb +95 -0
  26. data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
  27. data/lib/qonfig/commands/load_from_json.rb +56 -0
  28. data/lib/qonfig/commands/load_from_self.rb +73 -0
  29. data/lib/qonfig/commands/load_from_yaml.rb +58 -0
  30. data/lib/qonfig/configurable.rb +116 -0
  31. data/lib/qonfig/data_set.rb +213 -0
  32. data/lib/qonfig/data_set/class_builder.rb +27 -0
  33. data/lib/qonfig/data_set/validator.rb +7 -0
  34. data/lib/qonfig/dsl.rb +122 -0
  35. data/lib/qonfig/errors.rb +111 -0
  36. data/lib/qonfig/loaders.rb +9 -0
  37. data/lib/qonfig/loaders/basic.rb +38 -0
  38. data/lib/qonfig/loaders/json.rb +24 -0
  39. data/lib/qonfig/loaders/yaml.rb +24 -0
  40. data/lib/qonfig/plugins.rb +65 -0
  41. data/lib/qonfig/plugins/abstract.rb +13 -0
  42. data/lib/qonfig/plugins/access_mixin.rb +38 -0
  43. data/lib/qonfig/plugins/registry.rb +125 -0
  44. data/lib/qonfig/plugins/toml.rb +26 -0
  45. data/lib/qonfig/plugins/toml/commands/expose_toml.rb +146 -0
  46. data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +49 -0
  47. data/lib/qonfig/plugins/toml/data_set.rb +19 -0
  48. data/lib/qonfig/plugins/toml/dsl.rb +27 -0
  49. data/lib/qonfig/plugins/toml/loaders/toml.rb +24 -0
  50. data/lib/qonfig/plugins/toml/tomlrb_fixes.rb +92 -0
  51. data/lib/qonfig/plugins/toml/uploaders/toml.rb +25 -0
  52. data/lib/qonfig/settings.rb +457 -0
  53. data/lib/qonfig/settings/builder.rb +18 -0
  54. data/lib/qonfig/settings/key_guard.rb +71 -0
  55. data/lib/qonfig/settings/lock.rb +60 -0
  56. data/lib/qonfig/uploaders.rb +10 -0
  57. data/lib/qonfig/uploaders/base.rb +18 -0
  58. data/lib/qonfig/uploaders/file.rb +55 -0
  59. data/lib/qonfig/uploaders/json.rb +35 -0
  60. data/lib/qonfig/uploaders/yaml.rb +93 -0
  61. data/lib/qonfig/version.rb +7 -1
  62. data/qonfig.gemspec +29 -17
  63. metadata +122 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 640c084d4fd0dc167cfb9b52d12ae4d2f70382ec4d2f576703fc8be3a748fc08
4
- data.tar.gz: a36a0d612d46c542e9e20609a57c6fcf9ea244fce30fdbf295af8bc130ac9223
3
+ metadata.gz: 62b74a92c4c74b79f222e029726296a63b51ba3a57886a00911a34ffbcbced25
4
+ data.tar.gz: 33fa91631b6554dec3547c9c31faddf805bc4a56becbe7042e1942c19c493f16
5
5
  SHA512:
6
- metadata.gz: 3922f86f65e5d9486074c9ca425821a1d6d6ccb8e9655089c20e95696ece61a99a00e18aa19078b6fa71f069acad5558dcf3a622944c2b72348996774a8e79fc
7
- data.tar.gz: f71c819eafab31c35d1faa5cfeaa6a28c9fcee4898d2dc492385391c04e4c50c7376ca2f5a45560d86b25fa98bfab486122f970d9d86c70c77b318a98ab79ccb
6
+ metadata.gz: 152acb89e834fccaaed1b4bfae379f8263d795b2e4e7b7913f6a2b98fd1bfebf984dec1c82a3568ddc08a3e16fdec2be7783f1f583ef09a3dc6b1386f9afb18f
7
+ data.tar.gz: c468d5a5c289dc8b4b7b337e62314dbbf9d388b5dd6df1248a14e957a97be9937d7f24c47aa8634ffe415dbd092ed4f3c5b447036770b171fb6d87454eceb4e8
data/.gitignore CHANGED
@@ -6,6 +6,10 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
-
10
- # rspec failure tracking
11
9
  .rspec_status
10
+ Gemfile.lock
11
+ /.idea
12
+ .ruby-version
13
+ /.vscode/
14
+ /spec/artifacts/
15
+ /gemfiles/*.gemfile.lock
@@ -0,0 +1 @@
1
+ debug.fullTrace=true
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
- --format documentation
2
1
  --color
2
+ --format progress
3
3
  --require spec_helper
@@ -0,0 +1,15 @@
1
+ inherit_gem:
2
+ armitage-rubocop:
3
+ - lib/rubocop.general.yml
4
+ - lib/rubocop.rspec.yml
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 2.6.3
8
+ Include:
9
+ - lib/**/*.rb
10
+ - spec/**/*.rb
11
+ - Gemfile
12
+ - Rakefile
13
+ - qonfig.gemspec
14
+ - bin/console
15
+ - bin/rspec
@@ -1,5 +1,44 @@
1
- sudo: false
2
1
  language: ruby
3
- rvm:
4
- - 2.5.1
5
- before_install: gem install bundler -v 1.16.1
2
+ matrix:
3
+ fast_finish: true
4
+ include:
5
+ - rvm: 2.3.8
6
+ gemfile: gemfiles/with_external_deps.gemfile
7
+ env: TEST_PLUGINS=true
8
+ - rvm: 2.4.6
9
+ gemfile: gemfiles/with_external_deps.gemfile
10
+ env: TEST_PLUGINS=true
11
+ - rvm: 2.5.5
12
+ gemfile: gemfiles/with_external_deps.gemfile
13
+ env: TEST_PLUGINS=true
14
+ - rvm: 2.6.3
15
+ gemfile: gemfiles/with_external_deps.gemfile
16
+ env: TEST_PLUGINS=true
17
+ - rvm: ruby-head
18
+ gemfile: gemfiles/with_external_deps.gemfile
19
+ env: TEST_PLUGINS=true
20
+ - rvm: jruby-head
21
+ gemfile: gemfiles/with_external_deps.gemfile
22
+ env: TEST_PLUGINS=true
23
+ - rvm: 2.3.8
24
+ gemfile: gemfiles/without_external_deps.gemfile
25
+ - rvm: 2.4.6
26
+ gemfile: gemfiles/without_external_deps.gemfile
27
+ - rvm: 2.5.5
28
+ gemfile: gemfiles/without_external_deps.gemfile
29
+ - rvm: 2.6.3
30
+ gemfile: gemfiles/without_external_deps.gemfile
31
+ - rvm: ruby-head
32
+ gemfile: gemfiles/without_external_deps.gemfile
33
+ - rvm: jruby-head
34
+ gemfile: gemfiles/without_external_deps.gemfile
35
+ allow_failures:
36
+ - rvm: ruby-head
37
+ - rvm: jruby-head
38
+ sudo: false
39
+ cache: bundler
40
+ before_install: gem install bundler
41
+ script: bundle exec rspec
42
+ notifications:
43
+ slack:
44
+ secure: I03SDv+IrKy3IkeGjjHUJ9VneFMiYpLzIgPOixGFO5zGVXgulwmun82KsrPSW5HkK1UEQCahfoXt1hNECPwxcdsY01q6LBYJQx1jD0q17bXHllN/q0C6lx3peRN0KqNAAOU3/mHZxLt3HFV+N3HsSnoxDMuSYJgKbjCL/QVG2L2UYT9vi+JRNM/thj8R6MWKWlOHemik40GbLr2anCOCqiALzxnJzh5nJyGj+9SGvjhHfQq/fAIrg1+Scu+UchG8d+1yS5JWsGTt1/JF08i+Ux4ceTQ7GNBNeA5cj/xuUzvRx6A85renxyTiZMKIL0+jeceUm8c+/46XFcq0F7/kJB36lwjFhX1JRphcu/VouRdEwW/BvH74wnyHtygqOpk0LH4shp7A1DIS1DlnBbeJxrR5hdMDnmV85kTU6w6H0BbncsYY/pH1fji1kgH6jCsdwqDlq4wLB8x8I+eZABBsfb/r55z/HnBmmxlvR7Rt3X5/yR3gJrsgRrDBBZ5LwYy1RSCDu76vMQqWGJKG03FdfqSNqmC5V4MC5Ez8yjGDW0Yle09mwvsL2c6fDXyDPCeB/gwNk+FvgLRXnv7C4BaBNEoG4JnCL/dwauoJNg0lB6uF34BEmYAhZIrdY1CI6BqPWnaVMJfcJSYekBVXDIELDnTFRWjaGU1y8dM6lNzDmYE=
@@ -0,0 +1,121 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## [0.12.0] - 2019-07-19
5
+ ### Added
6
+ - Support for **TOML** (`.toml`) format
7
+ - realized as a plugin (`Qonfig.plugin(:toml)`);
8
+ - provides `#save_to_toml`, `#load_from_toml`, `#expose_toml` methods and works in `#*_yaml`-like manner);
9
+ - Custom `bin/rspec` command:
10
+ - `bin/rspec -n` - run tests without plugin tests;
11
+ - `bin/rspec -w` - run all tests;
12
+ - Added more convinient aliases for `Qonfig::DataSet` instances:
13
+ - `#save_to_yaml` => `#dump_to_yaml`;
14
+ - `#save_to_json` => `#dump_to_json`;
15
+ - `#save_to_toml` => `#dump_to_toml`;
16
+ ### Changed
17
+ - Actualized development dependencies;
18
+
19
+ ## [0.11.0] - 2019-05-15
20
+ ### Added
21
+ - `#save_to_json` - save configurations to a json file (uses native `::JSON.generate` under the hood);
22
+ - `#save_to_yaml` - save configurations to a yaml file (uses native `::Psych.dump` under the hood);
23
+
24
+ ### Changed
25
+ - new `#to_h` signature: `#to_h(key_transformer:, value_transformer:)`
26
+ - `:key_transformer` - proc object used for key pre-processing (`-> (key) { key }` by default);
27
+ - `:value_transformer` - proc object used for value pre-processing (`-> (value) { value }` by default);
28
+
29
+ ## [0.10.0] - 2019-02-26
30
+ ### Added
31
+ - `#slice_value` - get a slice of config options as a hash set and fetch the required value using the given key set;
32
+
33
+ ## [0.9.0] - 2018-11-28
34
+ ### Added
35
+ - `#slice` - get a slice of config options as a hash set (works in a `#dig` manner);
36
+
37
+ ## [0.8.0] - 2018-11-21
38
+ ### Changed
39
+ - `expose_yaml`, `load_from_yaml`, `load_from_json` and `load_from_self` treats empty hash (`{}`)
40
+ as an option with empty hash value (previously treated as a nested setting without options);
41
+
42
+ ## [0.7.0] - 2018-10-20
43
+ ### Added
44
+ - `expose_yaml` - a command that provides an ability to define config settings
45
+ by loading them from a yaml file where the concrete settings depends on the chosen environment;
46
+
47
+ ## [0.6.0] - 2018-08-22
48
+ ### Added
49
+ - `#shared_config` - instance method that provides an access to the class level config
50
+ object from `Qonfig::Configurable` instances;
51
+
52
+ ## [0.5.0] - 2018-07-27
53
+ ### Added
54
+ - `load_from_json`- a command that provides an ability to define config settings
55
+ by loading them from a json file (in `load_from_yaml` manner);
56
+
57
+ ### Changed
58
+ - Support for Ruby 2.2 has ended;
59
+
60
+ ## [0.4.0] - 2018-06-24
61
+ ### Added
62
+ - Introduce Plugin Ecosystem (`Qonfig::Plugins`):
63
+ - load plugin: `Qonfig.plugin('plugin_name')` or `Qonfig.plugin(:plugin_name)`;
64
+ - get registered plugins: `Qonfig.plugins #=> array of strings`
65
+
66
+ ## [0.3.0] - 2018-06-13
67
+ ### Added
68
+ - Improved configuration process: `#configure` can take a hash as a configuration `[option key => option]`
69
+ map of values;
70
+
71
+ ### Changed
72
+ - `#clear!` causes `Qonfig::FrozenSettingsError` if config object is frozen;
73
+
74
+ ## [0.2.0] - 2018-06-07
75
+ ### Added
76
+ - Instant configuration via block `config = Config.new { |conf| <<your configuration code>> }`;
77
+ - `.load_from_env` command - an ability to define config settings by loading them from ENV variable;
78
+ - `.load_from_yaml` command - an ability to define config settings by loading them from a yaml file;
79
+ - `.load_from_self` command - an ability to load config definitions form the YAML
80
+ instructions written in the file where the config class is defined (`__END__` section);
81
+ - `#reload!` - an ability to reload config isntance after any config class changes and updates;
82
+ - `#clear!` - an ability to set all options to `nil`;
83
+ - `#dig` - an ability to fetch setting values in `Hash#dig` manner
84
+ (fails with `Qonfig::UnknownSettingError` when the required key does not exist);
85
+ - Settings as Predicates - an ability to check the boolean nature of the config setting by appending
86
+ the question mark symbol (`?`) at the end of setting name:
87
+ - `nil` and `false` setting values indicates `false`;
88
+ - other setting values indicates `true`;
89
+ - setting roots always returns `true`;
90
+ - examples:
91
+ - `config.settings.database.user # => nil`;
92
+ - `config.settings.database.user? # => false`;
93
+ - `config.settings.database.host # => 'google.com'`;
94
+ - `config.settings.database.host? # => true`;
95
+ - `config.settings.database? # => true (setting with nested option (setting root))`
96
+ - Support for ERB instructions in YAML;
97
+ - Support for `HashWithIndifferentAccess`-like behaviour;
98
+ - `Qonfig::Settings` instance method redefinition protection: the setting key can not
99
+ have a name that matches an any instance method name of `Qonfig::Settings`;
100
+ - Added `Qonfig::Configurable` mixin - configuration behaviour for any classes and modules
101
+ and their instances:
102
+ - all `Qonfig`-related features;
103
+ - different class-level and instance-level config objects;
104
+ - working class-level inheritance :);
105
+ - Full thread-safe implementation;
106
+
107
+ ### Changed
108
+ - Superclass of `Qonfig::FrozenSettingsError` (it was `Qonfig::Error` before):
109
+ - `ruby >= 2.5` - inherited from `::FrozenError`;
110
+ - `ruby < 2.5` - inherited from `::RuntimeError`;
111
+ - `.setting` will raise exceptions immediately:
112
+ - `.setting(key, ...) { ... }` - if setting key has incompatible type;
113
+ - `.compose(config_class)`- if composed config class is not a subtype of `Qonfig::DataSet`;
114
+
115
+ ### Fixed
116
+ - Recoursive hash representation with deep nested `Qonfig::Settings` values (infinite loop);
117
+ - Fixed re-assignment of the options with nested options (losing the nested options
118
+ due to the instance configuration). Now it causes `Qonfig::AmbigousSettingValueError`.
119
+
120
+ ## [0.1.0] - 2018-05-18
121
+ - Release :)
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in qonfig.gemspec
6
8
  gemspec
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Rustam Ibragimov
3
+ Copyright (c) 2018-2019 Rustam Ibragimov
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,43 +1,1084 @@
1
- # Qonfig
1
+ # Qonfig &middot; [![Gem Version](https://badge.fury.io/rb/qonfig.svg)](https://badge.fury.io/rb/qonfig) [![Build Status](https://travis-ci.org/0exp/qonfig.svg?branch=master)](https://travis-ci.org/0exp/qonfig) [![Coverage Status](https://coveralls.io/repos/github/0exp/qonfig/badge.svg?branch=master)](https://coveralls.io/github/0exp/qonfig?branch=master)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/qonfig`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Config. Defined as a class. Used as an instance. Support for inheritance and composition.
4
+ Lazy instantiation. Thread-safe. Command-style DSL. Extremely simple to define. Extremely simple to use. That's all.
6
5
 
7
6
  ## Installation
8
7
 
9
- Add this line to your application's Gemfile:
10
-
11
8
  ```ruby
12
9
  gem 'qonfig'
13
10
  ```
14
11
 
15
- And then execute:
12
+ ```shell
13
+ $ bundle install
14
+ # --- or ---
15
+ $ gem install 'qonfig'
16
+ ```
17
+
18
+ ```ruby
19
+ require 'qonfig'
20
+ ```
21
+
22
+ ## Usage
16
23
 
17
- $ bundle
24
+ - [Definition and Settings Access](#definition-and-access)
25
+ - [Configuration](#configuration)
26
+ - [Inheritance](#inheritance)
27
+ - [Composition](#composition)
28
+ - [Hash representation](#hash-representation)
29
+ - [Config reloading](#config-reloading) (reload config definitions and option values)
30
+ - [Clear options](#clear-options) (set to nil)
31
+ - [State freeze](#state-freeze)
32
+ - [Settings as Predicates](#settings-as-predicates)
33
+ - [Load from YAML file](#load-from-yaml-file)
34
+ - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
35
+ - [Load from JSON file](#load-from-json-file)
36
+ - [Load from ENV](#load-from-env)
37
+ - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
38
+ - [Save to JSON file](#save-to-json-file) (`save_to_json`)
39
+ - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
40
+ - [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)
41
+ - [Plugins](#plugins)
42
+ - [toml](#plugin-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
43
+ ---
18
44
 
19
- Or install it yourself as:
45
+ ### Definition and Access
20
46
 
21
- $ gem install qonfig
47
+ ```ruby
48
+ # --- definition ---
49
+ class Config < Qonfig::DataSet
50
+ # nil by default
51
+ setting :project_id
22
52
 
23
- ## Usage
53
+ # nested setting
54
+ setting :vendor_api do
55
+ setting :host, 'app.service.com'
56
+ end
57
+
58
+ setting :enable_graphql, false
59
+
60
+ # nested setting reopening
61
+ setting :vendor_api do
62
+ setting :user, 'test_user'
63
+ end
64
+ end
65
+
66
+ config = Config.new # your configuration object instance
67
+
68
+ # --- setting access ---
69
+
70
+ # get option value via method
71
+ config.settings.project_id # => nil
72
+ config.settings.vendor_api.host # => 'app.service.com'
73
+ config.settings.vendor_api.user # => 'test_user'
74
+ config.settings.enable_graphql # => false
75
+
76
+ # get option value via index (with indifferent (string / symbol / mixed) access)
77
+ config.settings[:project_id] # => nil
78
+ config.settings[:vendor_api][:host] # => 'app.service.com'
79
+ config.settings[:vendor_api][:user] # => 'test_user'
80
+ config.settings[:enable_graphql] # => false
81
+
82
+ # get option value via index (with indifferent (string / symbol / mixed) access)
83
+ config.settings['project_id'] # => nil
84
+ config.settings['vendor_api']['host'] # => 'app.service.com'
85
+ config.settings['vendor_api']['user'] # => 'test_user'
86
+ config.settings['enable_graphql'] # => false
87
+
88
+ # get option value directly via index (with indifferent access)
89
+ config['project_id'] # => nil
90
+ config['enable_graphql'] # => false
91
+ config[:project_id] # => nil
92
+ config[:enable_graphql] # => false
93
+
94
+ # get option value in Hash#dig manner (and fail when the required key does not exist)
95
+ config.dig(:vendor_api, :host) # => 'app.service.com' # (key exists)
96
+ config.dig(:vendor_api, :port) # => Qonfig::UnknownSettingError # (key does not exist)
97
+
98
+ # get a hash slice of setting options (and fail when the required key does not exist)
99
+ config.slice(:vendor_api) # => { 'vendor_api' => { 'host' => 'app_service', 'user' => 'test_user' } }
100
+ config.slice(:vendor_api, :user) # => { 'user' => 'test_user' }
101
+ config.slice(:project_api) # => Qonfig::UnknownSettingError # (key does not exist)
102
+ config.slice(:vendor_api, :port) # => Qonfig::UnknownSettingError # (key does not exist)
103
+
104
+ # get value from the slice of setting options using the given key set (and fail when the required key does not exist) (works in slice manner)
105
+ config.slice_value(:vendor_api) # => { 'host' => 'app_service', 'user' => 'test_user' }
106
+ config.slice_value(:vendor_api, :user) # => 'test_user'
107
+ config.slice_value(:project_api) # => Qonfig::UnknownSettingError # (key does not exist)
108
+ config.slice_value(:vendor_api, :port) # => Qonfig::UnknownSettingError # (key does not exist)
109
+ ```
110
+
111
+ ---
112
+
113
+ ### Configuration
114
+
115
+ ```ruby
116
+ class Config < Qonfig::DataSet
117
+ setting :testing do
118
+ setting :engine, :rspec
119
+ setting :parallel, true
120
+ end
121
+
122
+ setting :geo_api do
123
+ setting :provider, :google_maps
124
+ end
125
+
126
+ setting :enable_middlewares, false
127
+ end
128
+
129
+ config = Config.new
130
+
131
+ # configure via proc
132
+ config.configure do |conf|
133
+ conf.enable_middlewares = true
134
+ conf.geo_api.provider = :yandex_maps
135
+ conf.testing.engine = :mini_test
136
+ end
137
+
138
+ # configure via settings object (by option name)
139
+ config.settings.enable_middlewares = false
140
+ config.settings.geo_api.provider = :apple_maps
141
+ config.settings.testing.engine = :ultra_test
142
+
143
+ # configure via settings object (by setting key)
144
+ config.settings[:enable_middlewares] = true
145
+ config.settings[:geo_api][:provider] = :rambler_maps
146
+ config.settings[:testing][:engine] = :mega_test
147
+
148
+ # instant configuration via proc
149
+ config = Config.new do |conf|
150
+ conf.enable_middlewares = false
151
+ conf.geo_api.provider = :amazon_maps
152
+ conf.testing.engine = :crypto_test
153
+ end
154
+
155
+ # using a hash
156
+ config = Config.new(
157
+ testing: { engine: :mini_test, parallel: false },
158
+ geo_api: { provider: :rambler_maps },
159
+ enable_middlewares: true
160
+ )
161
+ config.configure(enable_middlewares: false)
162
+
163
+ # using both hash and proc (proc has higher priority)
164
+ config = Config.new(enable_middlewares: true) do |conf|
165
+ conf.testing.parallel = true
166
+ end
167
+
168
+ config.configure(geo_api: { provider: nil }) do |conf|
169
+ conf.testing.engine = :rspec
170
+ end
171
+ ```
172
+
173
+ ---
174
+
175
+ ### Inheritance
176
+
177
+ ```ruby
178
+ class CommonConfig < Qonfig::DataSet
179
+ setting :uploader, :fog
180
+ end
181
+
182
+ class ProjectConfig < CommonConfig
183
+ setting :auth_provider, :github
184
+ end
185
+
186
+ project_config = ProjectConfig.new
187
+
188
+ # inherited setting
189
+ project_config.settings.uploader # => :fog
190
+
191
+ # own setting
192
+ project_config.settings.auth_provider # => :github
193
+ ```
194
+
195
+ ---
196
+
197
+ ### Composition
198
+
199
+ ```ruby
200
+ class SharedConfig < Qonfig::DataSet
201
+ setting :logger, Logger.new
202
+ end
203
+
204
+ class ServerConfig < Qonfig::DataSet
205
+ setting :port, 12345
206
+ setting :address, '0.0.0.0'
207
+ end
208
+
209
+ class DatabaseConfig < Qonfig::DataSet
210
+ setting :user, 'test'
211
+ setting :password, 'testpaswd'
212
+ end
213
+
214
+ class ProjectConfig < Qonfig::DataSet
215
+ compose SharedConfig
216
+
217
+ setting :server do
218
+ compose ServerConfig
219
+ end
220
+
221
+ setting :db do
222
+ compose DatabaseConfig
223
+ end
224
+ end
225
+
226
+ project_config = ProjectConfig.new
227
+
228
+ # fields from SharedConfig
229
+ project_config.settings.logger # => #<Logger:0x66f57048>
230
+
231
+ # fields from ServerConfig
232
+ project_config.settings.server.port # => 12345
233
+ project_config.settings.server.address # => '0.0.0.0'
234
+
235
+ # fields from DatabaseConfig
236
+ project_config.settings.db.user # => 'test'
237
+ project_config.settings.db.password # => 'testpaswd'
238
+ ```
239
+
240
+ ---
241
+
242
+ ### Hash representation
243
+
244
+ ```ruby
245
+ class Config < Qonfig::DataSet
246
+ setting :serializers do
247
+ setting :json do
248
+ setting :engine, :ok
249
+ end
250
+
251
+ setting :hash do
252
+ setting :engine, :native
253
+ end
254
+ end
255
+
256
+ setting :adapter do
257
+ setting :default, :memory_sync
258
+ end
259
+
260
+ setting :logger, Logger.new(STDOUT)
261
+ end
262
+
263
+ Config.new.to_h
264
+
265
+ {
266
+ "serializers": {
267
+ "json" => { "engine" => :ok },
268
+ "hash" => { "engine" => :native },
269
+ },
270
+ "adapter" => { "default" => :memory_sync },
271
+ "logger" => #<Logger:0x4b0d79fc>
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ### Config reloading
278
+
279
+ ```ruby
280
+ class Config < Qonfig::DataSet
281
+ setting :db do
282
+ setting :adapter, 'postgresql'
283
+ end
284
+
285
+ setting :logger, Logger.new(STDOUT)
286
+ end
287
+
288
+ config = Config.new
289
+
290
+ config.settings.db.adapter # => 'postgresql'
291
+ config.settings.logger # => #<Logger:0x00007ff9>
292
+
293
+ config.configure { |conf| conf.logger = nil } # redefine some settings (will be reloaded)
294
+
295
+ # re-define and append settings
296
+ class Config
297
+ setting :db do
298
+ setting :adapter, 'mongoid' # re-define defaults
299
+ end
300
+
301
+ setting :enable_api, false # append new setting
302
+ end
303
+
304
+ # reload settings
305
+ config.reload!
306
+
307
+ config.settings.db.adapter # => 'mongoid'
308
+ config.settings.logger # => #<Logger:0x00007ff9> (reloaded from defaults)
309
+ config.settings.enable_api # => false (new setting)
310
+
311
+ # reload with instant configuration
312
+ config.reload!(db: { adapter: 'oracle' }) do |conf|
313
+ conf.enable_api = true # changed instantly
314
+ end
315
+
316
+ config.settings.db.adapter # => 'oracle'
317
+ config.settings.logger = # => #<Logger:0x00007ff9>
318
+ config.settings.enable_api # => true # value from instant change
319
+ ```
320
+
321
+ ---
322
+
323
+ ### Clear options
324
+
325
+ ```ruby
326
+ class Config
327
+ setting :database do
328
+ setting :user
329
+ setting :password
330
+ end
331
+
332
+ setting :web_api do
333
+ setting :endpoint
334
+ end
335
+ end
336
+
337
+ config = Config.new do |conf|
338
+ conf.database.user = '0exp'
339
+ conf.database.password = 'test123'
340
+
341
+ conf.web_api.endpoint = '/api/'
342
+ end
343
+
344
+ config.settings.database.user # => '0exp'
345
+ config.settings.database.password # => 'test123'
346
+ config.settings.web_api.endpoint # => '/api'
347
+
348
+ # clear all options
349
+ config.clear!
350
+
351
+ config.settings.database.user # => nil
352
+ config.settings.database.password # => nil
353
+ config.settings.web_api.endpoint # => nil
354
+ ```
355
+
356
+ ---
357
+
358
+ ### State freeze
359
+
360
+ ```ruby
361
+ class Config < Qonfig::DataSet
362
+ setting :logger, Logger.new(STDOUT)
363
+ setting :worker, :sidekiq
364
+ setting :db do
365
+ setting :adapter, 'postgresql'
366
+ end
367
+ end
368
+
369
+ config = Config.new
370
+ config.freeze!
371
+
372
+ config.settings.logger = Logger.new(StringIO.new) # => Qonfig::FrozenSettingsError
373
+ config.settings.worker = :que # => Qonfig::FrozenSettingsError
374
+ config.settings.db.adapter = 'mongoid' # => Qonfig::FrozenSettingsError
375
+
376
+ config.reload! # => Qonfig::FrozenSettingsError
377
+ config.clear! # => Qonfig::FrozenSettingsError
378
+ ```
379
+
380
+ ---
381
+
382
+ ### Settings as Predicates
383
+
384
+ - predicate form: `?` at the end of setting name;
385
+ - `nil` and `false` setting values indicates `false`;
386
+ - other setting values indicates `true`;
387
+ - setting roots always returns `true`;
388
+
389
+ ```ruby
390
+ class Config < Qonfig::DataSet
391
+ setting :database do
392
+ setting :user
393
+ setting :host, 'google.com'
394
+
395
+ setting :engine do
396
+ setting :driver, 'postgres'
397
+ end
398
+ end
399
+ end
400
+
401
+ config = Config.new
402
+
403
+ # predicates
404
+ config.settings.database.user? # => false (nil => false)
405
+ config.settings.database.host? # => true ('google.com' => true)
406
+ config.settings.database.engine.driver? # => true ('postgres' => true)
407
+
408
+ # setting roots always returns true
409
+ config.settings.database? # => true
410
+ config.settings.database.engine? # => ture
411
+
412
+ config.configure do |conf|
413
+ conf.database.user = '0exp'
414
+ conf.database.host = false
415
+ conf.database.engine.driver = true
416
+ end
417
+
418
+ # predicates
419
+ config.settings.database.user? # => true ('0exp' => true)
420
+ config.settings.database.host? # => false (false => false)
421
+ config.settings.database.engine.driver? # => true (true => true)
422
+ ```
423
+
424
+ ---
425
+
426
+ ### Load from YAML file
427
+
428
+ - supports `ERB`;
429
+ - `:strict` mode (fail behaviour when the required yaml file doesnt exist):
430
+ - `true` (by default) - causes `Qonfig::FileNotFoundError`;
431
+ - `false` - do nothing, ignore current command;
432
+
433
+ ```yaml
434
+ # travis.yml
435
+
436
+ sudo: false
437
+ language: ruby
438
+ rvm:
439
+ - ruby-head
440
+ - jruby-head
441
+ ```
442
+
443
+ ```yaml
444
+ # project.yml
445
+
446
+ enable_api: false
447
+ Sidekiq/Scheduler:
448
+ enable: true
449
+ ```
450
+
451
+ ```yaml
452
+ # ruby_data.yml
453
+
454
+ version: <%= RUBY_VERSION %>
455
+ platform: <%= RUBY_PLATFORM %>
456
+ ```
457
+
458
+ ```ruby
459
+ class Config < Qonfig::DataSet
460
+ setting :ruby do
461
+ load_from_yaml 'ruby_data.yml'
462
+ end
463
+
464
+ setting :travis do
465
+ load_from_yaml 'travis.yml'
466
+ end
467
+
468
+ load_from_yaml 'project.yml'
469
+ end
470
+
471
+ config = Config.new
472
+
473
+ config.settings.travis.sudo # => false
474
+ config.settings.travis.language # => 'ruby'
475
+ config.settings.travis.rvm # => ['ruby-head', 'jruby-head']
476
+ config.settings.enable_api # => false
477
+ config.settings['Sidekiq/Scheduler']['enable'] #=> true
478
+ config.settings.ruby.version # => '2.5.1'
479
+ config.settings.ruby.platform # => 'x86_64-darwin17'
480
+ ```
481
+
482
+ ```ruby
483
+ # --- strict mode ---
484
+ class Config < Qonfig::DataSet
485
+ setting :nonexistent_yaml do
486
+ load_from_yaml 'nonexistent_yaml.yml', strict: true # true by default
487
+ end
488
+
489
+ setting :another_key
490
+ end
491
+
492
+ Config.new # => Qonfig::FileNotFoundError
493
+
494
+ # --- non-strict mode ---
495
+ class Config < Qonfig::DataSet
496
+ settings :nonexistent_yaml do
497
+ load_from_yaml 'nonexistent_yaml.yml', strict: false
498
+ end
499
+
500
+ setting :another_key
501
+ end
502
+
503
+ Config.new.to_h # => { "nonexistent_yaml" => {}, "another_key" => nil }
504
+ ```
505
+
506
+ ---
507
+
508
+ ### Expose YAML
509
+
510
+ - load configurations from YAML file in Rails-like manner (with environments);
511
+ - works in `load_from_yaml` manner;
512
+ - `via:` - how an environment will be determined:
513
+ - `:file_name`
514
+ - load configuration from YAML file that have an `:env` part in it's name;
515
+ - `:env_key`
516
+ - load configuration from YAML file;
517
+ - concrete configuration should be defined in the root key with `:env` name;
518
+ - `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);
519
+ - `strict:` - requires the existence of the file and/or key with the name of the used environment:
520
+ - `true`:
521
+ - file should exist;
522
+ - root key with `:env` name should exist (if `via: :env_key` is used);
523
+ - raises `Qonfig::ExposeError` if file does not contain the required env key (if `via: :env` key is used);
524
+ - raises `Qonfig::FileNotFoundError` if the required file does not exist;
525
+ - `false`:
526
+ - file is not required;
527
+ - root key with `:env` name is not required (if `via: :env_key` is used);
528
+
529
+ #### Environment is defined as a root key of YAML file
530
+
531
+ ```yaml
532
+ # config/project.yml
533
+
534
+ default: &default
535
+ enable_api_mode: true
536
+ google_key: 12345
537
+ window:
538
+ width: 100
539
+ height: 100
24
540
 
25
- TODO: Write usage instructions here
541
+ development:
542
+ <<: *default
26
543
 
27
- ## Development
544
+ test:
545
+ <<: *default
546
+ sidekiq_instrumentation: false
547
+
548
+ staging:
549
+ <<: *default
550
+ google_key: 777
551
+ enable_api_mode: false
552
+
553
+ production:
554
+ google_key: asd1-39sd-55aI-O92x
555
+ enable_api_mode: true
556
+ window:
557
+ width: 50
558
+ height: 150
559
+ ```
560
+
561
+ ```ruby
562
+ class Config < Qonfig::DataSet
563
+ expose_yaml 'config/project.yml', via: :env_key, env: :production # load from production env
564
+
565
+ # NOTE: in rails-like application you can use this:
566
+ expose_yaml 'config/project.yml', via: :env_key, env: Rails.env
567
+ end
568
+
569
+ config = Config.new
570
+
571
+ config.settings.enable_api_mode # => true (from :production subset of keys)
572
+ config.settings.google_key # => asd1-39sd-55aI-O92x (from :production subset of keys)
573
+ config.settings.window.width # => 50 (from :production subset of keys)
574
+ config.settings.window.height # => 150 (from :production subset of keys)
575
+ ```
576
+
577
+ #### Environment is defined as a part of YAML file name
578
+
579
+ ```yaml
580
+ # config/sidekiq.staging.yml
581
+
582
+ web:
583
+ username: staging_admin
584
+ password: staging_password
585
+ ```
586
+
587
+ ```yaml
588
+ # config/sidekiq.production.yml
589
+
590
+ web:
591
+ username: urj1o2
592
+ password: u192jd0ixz0
593
+ ```
594
+
595
+ ```ruby
596
+ class SidekiqConfig < Qonfig::DataSet
597
+ # NOTE: file name should be described WITHOUT environment part (in file name attribute)
598
+ expose_yaml 'config/sidekiq.yml', via: :file_name, env: :staging # load from staging env
599
+
600
+ # NOTE: in rails-like application you can use this:
601
+ expose_yaml 'config/sidekiq.yml', via: :file_name, env: Rails.env
602
+ end
603
+
604
+ config = SidekiqConfig.new
605
+
606
+ config.settings.web.username # => staging_admin (from sidekiq.staging.yml)
607
+ config.settings.web.password # => staging_password (from sidekiq.staging.yml)
608
+ ```
609
+
610
+ ---
611
+
612
+ ### Load from JSON file
613
+
614
+ - `:strict` mode (fail behaviour when the required yaml file doesnt exist):
615
+ - `true` (by default) - causes `Qonfig::FileNotFoundError`;
616
+ - `false` - do nothing, ignore current command;
617
+
618
+ ```json
619
+ // options.json
620
+
621
+ {
622
+ "user": "0exp",
623
+ "password": 12345,
624
+ "rubySettings": {
625
+ "allowedVersions": ["2.3", "2.4.2", "1.9.8"],
626
+ "gitLink": null,
627
+ "withAdditionals": false
628
+ }
629
+ }
630
+ ```
631
+
632
+ ```ruby
633
+ class Config < Qonfig::DataSet
634
+ load_from_json 'options.json'
635
+ end
636
+
637
+ config = Config.new
638
+
639
+ config.settings.user # => '0exp'
640
+ config.settings.password # => 12345
641
+ config.settings.rubySettings.allowedVersions # => ['2.3', '2.4.2', '1.9.8']
642
+ config.settings.rubySettings.gitLink # => nil
643
+ config.settings.rubySettings.withAdditionals # => false
644
+ ```
645
+
646
+ ```ruby
647
+ # --- strict mode ---
648
+ class Config < Qonfig::DataSet
649
+ setting :nonexistent_json do
650
+ load_from_json 'nonexistent_json.json', strict: true # true by default
651
+ end
652
+
653
+ setting :another_key
654
+ end
655
+
656
+ Config.new # => Qonfig::FileNotFoundError
657
+
658
+ # --- non-strict mode ---
659
+ class Config < Qonfig::DataSet
660
+ settings :nonexistent_json do
661
+ load_from_json 'nonexistent_json.json', strict: false
662
+ end
663
+
664
+ setting :another_key
665
+ end
666
+
667
+ Config.new.to_h # => { "nonexistent_json" => {}, "another_key" => nil }
668
+ ```
669
+
670
+ ---
671
+
672
+ ### Load from ENV
673
+
674
+ - `:convert_values` (`false` by default):
675
+ - `'t'`, `'T'`, `'true'`, `'TRUE'` - covnerts to `true`;
676
+ - `'f'`, `'F'`, `'false'`, `'FALSE'` - covnerts to `false`;
677
+ - `1`, `23` and etc - converts to `Integer`;
678
+ - `1.25`, `0.26` and etc - converts to `Float`;
679
+ - `1, 2, test`, `FALSE,Qonfig` (strings without quotes that contains at least one comma) -
680
+ converts to `Array` with recursively converted values;
681
+ - `'"please, test"'`, `"'test, please'"` (quoted strings) - converts to `String` without quotes;
682
+ - `:prefix` - load ENV variables which names starts with a prefix:
683
+ - `nil` (by default) - empty prefix;
684
+ - `Regexp` - names that match the regexp pattern;
685
+ - `String` - names which starts with a passed string;
686
+ - `:trim_prefix` (`false` by default);
687
+
688
+ ```ruby
689
+ # some env variables
690
+ ENV['QONFIG_BOOLEAN'] = 'true'
691
+ ENV['QONFIG_INTEGER'] = '0'
692
+ ENV['QONFIG_STRING'] = 'none'
693
+ ENV['QONFIG_ARRAY'] = '1, 2.5, t, f, TEST'
694
+ ENV['QONFIG_MESSAGE'] = '"Hello, Qonfig!"'
695
+ ENV['RUN_CI'] = '1'
696
+
697
+ class Config < Qonfig::DataSet
698
+ # nested
699
+ setting :qonfig do
700
+ load_from_env convert_values: true, prefix: 'QONFIG' # or /\Aqonfig.*\z/i
701
+ end
702
+
703
+ setting :trimmed do
704
+ load_from_env convert_values: true, prefix: 'QONFIG_', trim_prefix: true # trim prefix
705
+ end
706
+
707
+ # on the root
708
+ load_from_env
709
+ end
710
+
711
+ config = Config.new
712
+
713
+ # customized
714
+ config.settings['qonfig']['QONFIG_BOOLEAN'] # => true ('true' => true)
715
+ config.settings['qonfig']['QONFIG_INTEGER'] # => 0 ('0' => 0)
716
+ config.settings['qonfig']['QONFIG_STRING'] # => 'none'
717
+ config.settings['qonfig']['QONFIG_ARRAY'] # => [1, 2.5, true, false, 'TEST']
718
+ config.settings['qonfig']['QONFIG_MESSAGE'] # => 'Hello, Qonfig!'
719
+ config.settings['qonfig']['RUN_CI'] # => Qonfig::UnknownSettingError
720
+
721
+ # trimmed (and customized)
722
+ config.settings['trimmed']['BOOLEAN'] # => true ('true' => true)
723
+ config.settings['trimmed']['INTEGER'] # => 0 ('0' => 0)
724
+ config.settings['trimmed']['STRING'] # => 'none'
725
+ config.settings['trimmed']['ARRAY'] # => [1, 2.5, true, false, 'TEST']
726
+ config.settings['trimmed']['MESSAGE'] # => 'Hello, Qonfig!'
727
+ config.settings['trimmed']['RUN_CI'] # => Qonfig::UnknownSettingError
728
+
729
+ # default
730
+ config.settings['QONFIG_BOOLEAN'] # => 'true'
731
+ config.settings['QONFIG_INTEGER'] # => '0'
732
+ config.settings['QONFIG_STRING'] # => 'none'
733
+ config.settings['QONFIG_ARRAY'] # => '1, 2.5, t, f, TEST'
734
+ config.settings['QONFIG_MESSAGE'] # => '"Hello, Qonfig!"'
735
+ config.settings['RUN_CI'] # => '1'
736
+ ```
737
+
738
+ ---
739
+
740
+ ### Load from \_\_END\_\_
741
+
742
+ - aka `load_from_self`
743
+
744
+ ```ruby
745
+ class Config < Qonfig::DataSet
746
+ load_from_self # on the root
747
+
748
+ setting :nested do
749
+ load_from_self # nested
750
+ end
751
+ end
752
+
753
+ config = Config.new
754
+
755
+ # on the root
756
+ config.settings.ruby_version # => '2.5.1'
757
+ config.settings.secret_key # => 'top-mega-secret'
758
+ config.settings.api_host # => 'super.puper-google.com'
759
+ config.settings.connection_timeout.seconds # => 10
760
+ config.settings.connection_timeout.enabled # => false
761
+
762
+ # nested
763
+ config.settings.nested.ruby_version # => '2.5.1'
764
+ config.settings.nested.secret_key # => 'top-mega-secret'
765
+ config.settings.nested.api_host # => 'super.puper-google.com'
766
+ config.settings.nested.connection_timeout.seconds # => 10
767
+ config.settings.nested.connection_timeout.enabled # => false
768
+
769
+ __END__
770
+
771
+ ruby_version: <%= RUBY_VERSION %>
772
+ secret_key: top-mega-secret
773
+ api_host: super.puper-google.com
774
+ connection_timeout:
775
+ seconds: 10
776
+ enabled: false
777
+ ```
778
+
779
+ ---
780
+
781
+ ### Save to JSON file
782
+
783
+ - `#save_to_json` - represents config object as a json structure and saves it to a file:
784
+ - uses native `::JSON.generate` under the hood;
785
+ - writes new file (or rewrites existing file);
786
+ - attributes:
787
+ - `:path` - (required) - file path;
788
+ - `:options` - (optional) - native `::JSON.generate` options (from stdlib):
789
+ - `:indent` - `" "` by default;
790
+ - `:space` - `" "` by default/
791
+ - `:object_nl` - `"\n"` by default;
792
+ - `&value_preprocessor` - (optional) - value pre-processor;
793
+
794
+ #### Without value preprocessing (standard usage)
795
+
796
+ ```ruby
797
+ class AppConfig < Qonfig::DataSet
798
+ setting :server do
799
+ setting :address, 'localhost'
800
+ setting :port, 12_345
801
+ end
802
+
803
+ setting :enabled, true
804
+ end
805
+
806
+ config = AppConfig.new
807
+
808
+ # NOTE: save to json file
809
+ config.save_to_json(path: 'config.json')
810
+ ```
811
+
812
+ ```json
813
+ {
814
+ "sentry": {
815
+ "address": "localhost",
816
+ "port": 12345
817
+ },
818
+ "enabled": true
819
+ }
820
+ ```
821
+
822
+ #### With value preprocessing and custom options
823
+
824
+ ```ruby
825
+ class AppConfig < Qonfig::DataSet
826
+ setting :server do
827
+ setting :address, 'localhost'
828
+ setting :port, 12_345
829
+ end
830
+
831
+ setting :enabled, true
832
+ setting :dynamic, -> { 1 + 2 }
833
+ end
834
+
835
+ config = AppConfig.new
836
+
837
+ # NOTE: save to json file with custom options (no spaces / no new line / no indent; call procs)
838
+ config.save_to_json(path: 'config.json', options: { indent: '', space: '', object_nl: '' }) do |value|
839
+ value.is_a?(Proc) ? value.call : value
840
+ end
841
+ ```
842
+
843
+ ```json
844
+ // no spaces / no new line / no indent / calculated "dynamic" setting key
845
+ {"sentry":{"address":"localhost","port":12345},"enabled":true,"dynamic":3}
846
+ ```
847
+
848
+ ---
849
+
850
+ ### Save to YAML file
851
+
852
+ - `#save_to_yaml` - represents config object as a yaml structure and saves it to a file:
853
+ - uses native `::Psych.dump` under the hood;
854
+ - writes new file (or rewrites existing file);
855
+ - attributes:
856
+ - `:path` - (required) - file path;
857
+ - `:options` - (optional) - native `::Psych.dump` options (from stdlib):
858
+ - `:indentation` - `2` by default;
859
+ - `:line_width` - `-1` by default;
860
+ - `:canonical` - `false` by default;
861
+ - `:header` - `false` by default;
862
+ - `:symbolize_keys` - (non-native option) - `false` by default;
863
+ - `&value_preprocessor` - (optional) - value pre-processor;
864
+
865
+ #### Without value preprocessing (standard usage)
866
+
867
+ ```ruby
868
+ class AppConfig < Qonfig::DataSet
869
+ setting :server do
870
+ setting :address, 'localhost'
871
+ setting :port, 12_345
872
+ end
873
+
874
+ setting :enabled, true
875
+ end
876
+
877
+ config = AppConfig.new
878
+
879
+ # NOTE: save to yaml file
880
+ config.save_to_yaml(path: 'config.yml')
881
+ ```
882
+
883
+ ```yaml
884
+ ---
885
+ server:
886
+ address: localhost
887
+ port: 12345
888
+ enabled: true
889
+ ```
890
+
891
+ #### With value preprocessing and custom options
892
+
893
+ ```ruby
894
+ class AppConfig < Qonfig::DataSet
895
+ setting :server do
896
+ setting :address, 'localhost'
897
+ setting :port, 12_345
898
+ end
899
+
900
+ setting :enabled, true
901
+ setting :dynamic, -> { 5 + 5 }
902
+ end
903
+
904
+ config = AppConfig.new
905
+
906
+ # NOTE: save to yaml file with custom options (add yaml version header; call procs)
907
+ config.save_to_yaml(path: 'config.yml', options: { header: true }) do |value|
908
+ value.is_a?(Proc) ? value.call : value
909
+ end
910
+ ```
911
+
912
+ ```yaml
913
+ # yaml version header / calculated "dynamic" setting key
914
+ %YAML 1.1
915
+ ---
916
+ server:
917
+ address: localhost
918
+ port: 12345
919
+ enabled: true
920
+ dynamic: 10
921
+ ```
922
+
923
+ ---
924
+
925
+ ### Smart Mixin
926
+
927
+ - class-level:
928
+ - `.configuration` - settings definitions;
929
+ - `.configure` - configuration;
930
+ - `.config` - config object;
931
+ - settings definitions are inheritable;
932
+ - instance-level:
933
+ - `#configure` - configuration;
934
+ - `#config` - config object;
935
+ - `#shared_config` - class-level config object;
936
+
937
+ ```ruby
938
+ # --- usage ---
939
+
940
+ class Application
941
+ # make configurable
942
+ include Qonfig::Configurable
943
+
944
+ configuration do
945
+ setting :user
946
+ setting :password
947
+ end
948
+ end
949
+
950
+ app = Application.new
951
+
952
+ # class-level config
953
+ Application.config.settings.user # => nil
954
+ Application.config.settings.password # => nil
955
+
956
+ # instance-level config
957
+ app.config.settings.user # => nil
958
+ app.config.settings.password # => nil
959
+
960
+ # access to the class level config from an instance
961
+ app.shared_config.settings.user # => nil
962
+ app.shared_config.settings.password # => nil
963
+
964
+ # class-level configuration
965
+ Application.configure do |conf|
966
+ conf.user = '0exp'
967
+ conf.password = 'test123'
968
+ end
969
+
970
+ # instance-level configuration
971
+ app.configure do |conf|
972
+ conf.user = 'admin'
973
+ conf.password = '123test'
974
+ end
975
+
976
+ # class has own config object
977
+ Application.config.settings.user # => '0exp'
978
+ Application.config.settings.password # => 'test123'
979
+
980
+ # instance has own config object
981
+ app.config.settings.user # => 'admin'
982
+ app.config.settings.password # => '123test'
983
+
984
+ # access to the class level config from an instance
985
+ app.shared_config.settings.user # => '0exp'
986
+ app.shared_config.settings.password # => 'test123'
987
+
988
+ # and etc... (all Qonfig-related features)
989
+ ```
990
+
991
+ ```ruby
992
+ # --- inheritance ---
993
+
994
+ class BasicApplication
995
+ # make configurable
996
+ include Qonfig::Configurable
997
+
998
+ configuration do
999
+ setting :user
1000
+ setting :pswd
1001
+ end
1002
+
1003
+ configure do |conf|
1004
+ conf.user = 'admin'
1005
+ conf.pswd = 'admin'
1006
+ end
1007
+ end
1008
+
1009
+ class GeneralApplication < BasicApplication
1010
+ # extend inherited definitions
1011
+ configuration do
1012
+ setting :db do
1013
+ setting :adapter
1014
+ end
1015
+ end
1016
+
1017
+ configure do |conf|
1018
+ conf.user = '0exp' # .user inherited from BasicApplication
1019
+ conf.pswd = '123test' # .pswd inherited from BasicApplication
1020
+ conf.db.adapter = 'pg'
1021
+ end
1022
+ end
1023
+
1024
+ BasicApplication.config.to_h
1025
+ { 'user' => 'admin', 'pswd' => 'admin' }
1026
+
1027
+ GeneralApplication.config.to_h
1028
+ { 'user' => '0exp', 'pswd' => '123test', 'db' => { 'adapter' => 'pg' } }
1029
+
1030
+ # and etc... (all Qonfig-related features)
1031
+ ```
1032
+
1033
+ ---
1034
+
1035
+ ### Plugins
1036
+
1037
+ ```ruby
1038
+ # --- show names of registered plugins ---
1039
+ Qonfig.plugins # => array of strings
1040
+
1041
+ # --- load specific plugin ---
1042
+ Qonfig.plugin(:plugin_name) # or Qonfig.plugin('plugin_name')
1043
+ ```
1044
+
1045
+ ---
1046
+
1047
+ ### Plugins: toml
1048
+
1049
+ - adds support for `toml` format ([specification](https://github.com/toml-lang/toml));
1050
+ - depends on `toml-rb` gem;
1051
+ - provides `load_from_toml` (works in `load_from_yaml` manner [doc](#load-from-yaml-file));
1052
+ - provides `save_to_toml` (works in `save_to_yaml` manner [doc](#save-to-yaml-file)) (`toml-rb` has no native options);
1053
+ - provides `expose_toml` (works in `expose_yaml` manner [doc](#expose-yaml));
1054
+
1055
+ ```ruby
1056
+ require 'toml-rb'
1057
+ Qonfig.plugin(:toml)
1058
+ # and use :)
1059
+ ```
1060
+ ---
28
1061
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
1062
+ ## Roadmap
30
1063
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
1064
+ - support for TOML format;
1065
+ - explicit "settings" object;
1066
+ - validation layer;
1067
+ - distributed configuration server;
1068
+ - support for Rails-like secrets;
32
1069
 
33
1070
  ## Contributing
34
1071
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/qonfig. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
1072
+ - Fork it ( https://github.com/0exp/qonfig/fork )
1073
+ - Create your feature branch (`git checkout -b feature/my-new-feature`)
1074
+ - Commit your changes (`git commit -am 'Add some feature'`)
1075
+ - Push to the branch (`git push origin feature/my-new-feature`)
1076
+ - Create new Pull Request
36
1077
 
37
1078
  ## License
38
1079
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
1080
+ Released under MIT License.
40
1081
 
41
- ## Code of Conduct
1082
+ ## Authors
42
1083
 
43
- Everyone interacting in the Qonfig project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/qonfig/blob/master/CODE_OF_CONDUCT.md).
1084
+ [Rustam Ibragimov](https://github.com/0exp)