settings_reader 0.1.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.
- checksums.yaml +7 -0
- data/.github/workflows/linters.yml +52 -0
- data/.github/workflows/main.yml +66 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +12 -0
- data/.simplecov +9 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +13 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +69 -0
- data/LICENSE.txt +21 -0
- data/README.md +222 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/settings_reader/backends/abstract.rb +14 -0
- data/lib/settings_reader/backends/yaml_file.rb +31 -0
- data/lib/settings_reader/configuration.rb +20 -0
- data/lib/settings_reader/mixins/path.rb +21 -0
- data/lib/settings_reader/mixins/values.rb +43 -0
- data/lib/settings_reader/reader.rb +53 -0
- data/lib/settings_reader/resolvers/abstract.rb +18 -0
- data/lib/settings_reader/resolvers/env.rb +21 -0
- data/lib/settings_reader/resolvers/erb.rb +20 -0
- data/lib/settings_reader/version.rb +3 -0
- data/lib/settings_reader.rb +21 -0
- data/settings_reader.gemspec +39 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ebad4556c91f82603c3811808d396bbc12875352e8f19acee09ad4787b958edc
|
4
|
+
data.tar.gz: 0a1e35282c1d2ce2b1ea49be238d2dda10ba7877ca5d066526f6981d3826f639
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e01df5eea2864552cde89b3bebb194ee762a0573c2b782a1fba4d7525e4729f359643651e168090f0e09547bb4c6c625d1d9b68e4b5cd5dde8696def39d288a4
|
7
|
+
data.tar.gz: 681a99042b9b582935478b2f8a8d2455baa156e5c027700cab87d857767f9abba523c540a9ab42ad17bd47bf94a9d21ef6ac8b73a469dd8dc4ea7354fea7010b
|
@@ -0,0 +1,52 @@
|
|
1
|
+
name: "linters"
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ main ]
|
8
|
+
schedule:
|
9
|
+
- cron: '30 0 * * 1'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
rubocop:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- name: Checkout
|
17
|
+
uses: actions/checkout@v2
|
18
|
+
- name: Set up Ruby
|
19
|
+
uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: 2.5
|
22
|
+
bundler-cache: true
|
23
|
+
- name: Run rubocop
|
24
|
+
run: bundle exec rubocop --parallel
|
25
|
+
|
26
|
+
code-ql:
|
27
|
+
name: Analyze
|
28
|
+
runs-on: ubuntu-latest
|
29
|
+
permissions:
|
30
|
+
actions: read
|
31
|
+
contents: read
|
32
|
+
security-events: write
|
33
|
+
|
34
|
+
strategy:
|
35
|
+
fail-fast: false
|
36
|
+
matrix:
|
37
|
+
language: [ 'ruby' ]
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- name: Checkout repository
|
41
|
+
uses: actions/checkout@v2
|
42
|
+
|
43
|
+
- name: Initialize CodeQL
|
44
|
+
uses: github/codeql-action/init@v1
|
45
|
+
with:
|
46
|
+
languages: ${{ matrix.language }}
|
47
|
+
|
48
|
+
- name: Autobuild
|
49
|
+
uses: github/codeql-action/autobuild@v1
|
50
|
+
|
51
|
+
- name: Perform CodeQL Analysis
|
52
|
+
uses: github/codeql-action/analyze@v1
|
@@ -0,0 +1,66 @@
|
|
1
|
+
name: ci
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- main
|
6
|
+
pull_request:
|
7
|
+
branches:
|
8
|
+
- main
|
9
|
+
release:
|
10
|
+
types: [published]
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
build:
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
strategy:
|
16
|
+
matrix:
|
17
|
+
ruby: [ '2.5', '2.6', '2.7', '3.0' ]
|
18
|
+
steps:
|
19
|
+
- name: Checkout
|
20
|
+
uses: actions/checkout@v1
|
21
|
+
|
22
|
+
- name: Set up Ruby
|
23
|
+
uses: ruby/setup-ruby@v1
|
24
|
+
with:
|
25
|
+
ruby-version: ${{ matrix.ruby }}
|
26
|
+
bundler-cache: true
|
27
|
+
|
28
|
+
- name: Run specs
|
29
|
+
env:
|
30
|
+
COVERAGE: true
|
31
|
+
run: bundle exec rspec
|
32
|
+
|
33
|
+
- name: Upload coverage
|
34
|
+
env:
|
35
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
36
|
+
run: bash <(curl -s https://codecov.io/bash)
|
37
|
+
|
38
|
+
release:
|
39
|
+
runs-on: ubuntu-latest
|
40
|
+
needs: build
|
41
|
+
if: github.event_name == 'release' && github.event.action == 'published'
|
42
|
+
steps:
|
43
|
+
- name: Checkout
|
44
|
+
uses: actions/checkout@v1
|
45
|
+
|
46
|
+
- name: Set up Ruby
|
47
|
+
uses: ruby/setup-ruby@v1
|
48
|
+
with:
|
49
|
+
ruby-version: 2.7
|
50
|
+
bundler-cache: true
|
51
|
+
- name: Set up credentials
|
52
|
+
run: |
|
53
|
+
mkdir -p $HOME/.gem
|
54
|
+
touch $HOME/.gem/credentials
|
55
|
+
chmod 0600 $HOME/.gem/credentials
|
56
|
+
printf -- "---\n:rubygems_api_key: ${{secrets.RUBYGEMS_AUTH_TOKEN}}\n" > $HOME/.gem/credentials
|
57
|
+
|
58
|
+
- name: Get version
|
59
|
+
run: echo "${GITHUB_REF/refs\/tags\//}" > release.tag
|
60
|
+
- name: Set version
|
61
|
+
run: sed -i "s/0.0.0/$(<release.tag)/g" */**/version.rb
|
62
|
+
|
63
|
+
- name: Build gem
|
64
|
+
run: gem build *.gemspec
|
65
|
+
- name: Push gem
|
66
|
+
run: gem push *.gem
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.simplecov
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [0.1.0]
|
4
|
+
Initial Release
|
5
|
+
|
6
|
+
### New features
|
7
|
+
- Backends & Resolvers
|
8
|
+
- Simplified interface
|
9
|
+
- Plugin system
|
10
|
+
|
11
|
+
[Unreleased]: https://github.com/matic-insurance/settings_reader-vault_resolver/compare/0.1.0...HEAD
|
12
|
+
[0.1.0]: https://github.com/matic-insurance/settings_reader-vault_resolver/commits/0.1.0
|
13
|
+
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at 712680+volodymyr-mykhailyk@users.noreply.github.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: https://contributor-covenant.org
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
settings_reader (0.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
codecov (0.6.0)
|
11
|
+
simplecov (>= 0.15, < 0.22)
|
12
|
+
diff-lcs (1.5.0)
|
13
|
+
docile (1.4.0)
|
14
|
+
parallel (1.21.0)
|
15
|
+
parser (3.1.0.0)
|
16
|
+
ast (~> 2.4.1)
|
17
|
+
rainbow (3.1.1)
|
18
|
+
rake (13.0.6)
|
19
|
+
regexp_parser (2.2.0)
|
20
|
+
rexml (3.2.5)
|
21
|
+
rspec (3.10.0)
|
22
|
+
rspec-core (~> 3.10.0)
|
23
|
+
rspec-expectations (~> 3.10.0)
|
24
|
+
rspec-mocks (~> 3.10.0)
|
25
|
+
rspec-core (3.10.1)
|
26
|
+
rspec-support (~> 3.10.0)
|
27
|
+
rspec-expectations (3.10.2)
|
28
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
+
rspec-support (~> 3.10.0)
|
30
|
+
rspec-mocks (3.10.2)
|
31
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
+
rspec-support (~> 3.10.0)
|
33
|
+
rspec-support (3.10.3)
|
34
|
+
rubocop (0.93.1)
|
35
|
+
parallel (~> 1.10)
|
36
|
+
parser (>= 2.7.1.5)
|
37
|
+
rainbow (>= 2.2.2, < 4.0)
|
38
|
+
regexp_parser (>= 1.8)
|
39
|
+
rexml
|
40
|
+
rubocop-ast (>= 0.6.0)
|
41
|
+
ruby-progressbar (~> 1.7)
|
42
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
43
|
+
rubocop-ast (1.15.1)
|
44
|
+
parser (>= 3.0.1.1)
|
45
|
+
rubocop-rspec (1.32.0)
|
46
|
+
rubocop (>= 0.60.0)
|
47
|
+
ruby-progressbar (1.11.0)
|
48
|
+
simplecov (0.21.2)
|
49
|
+
docile (~> 1.1)
|
50
|
+
simplecov-html (~> 0.11)
|
51
|
+
simplecov_json_formatter (~> 0.1)
|
52
|
+
simplecov-html (0.12.3)
|
53
|
+
simplecov_json_formatter (0.1.3)
|
54
|
+
unicode-display_width (1.8.0)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
codecov (~> 0.4)
|
61
|
+
rake (~> 13.0)
|
62
|
+
rspec (~> 3.0)
|
63
|
+
rubocop (~> 0.66)
|
64
|
+
rubocop-rspec (~> 1.32.0)
|
65
|
+
settings_reader!
|
66
|
+
simplecov (~> 0.16)
|
67
|
+
|
68
|
+
BUNDLED WITH
|
69
|
+
2.1.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Volodymyr Mykhailyk
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
# SettingsReader
|
2
|
+
|
3
|
+

|
4
|
+
[](https://codecov.io/gh/matic-insurance/settings_reader)
|
5
|
+
|
6
|
+
Settings Reader provides flexible way to make settings available for any application.
|
7
|
+
|
8
|
+
Settings are retrieved in 2 steps:
|
9
|
+
1. Get value from one of Backends (Yaml, KV storage, Database, etc)
|
10
|
+
2. Process value using one of Resolver (Environment variable, Erb template, Vault, etc)
|
11
|
+
|
12
|
+
Gem support any number of backends and resolvers.
|
13
|
+
Such scheme allows customized and flexible settings for any environment. For example:
|
14
|
+
- Read value in Consul, fallback to yaml, resolve via ERB for additional flexibility
|
15
|
+
- Read value in Yaml for specific environment (local file), fallback to generic Yaml config, resolve in env when deployed
|
16
|
+
|
17
|
+
The gem is built around an idea that having full set of settings in the repository allows any maintainer of app
|
18
|
+
to better understand how it works. At the same time providing flexibility of where the settings will be retrieved/resolved
|
19
|
+
in the end environment (local dev machine, production instance, k8s pod).
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'settings_reader'
|
27
|
+
```
|
28
|
+
|
29
|
+
## Initialization
|
30
|
+
|
31
|
+
At the load of application configure and load settings:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
APP_SETTINGS = SettingsReader.load('my_cool_app') do |config|
|
35
|
+
# Configure backends.
|
36
|
+
config.backends = [
|
37
|
+
SettingsReader::Backends::YamlFile.new(Rails.root.join("config/settings/#{Rails.env}.yml")),
|
38
|
+
SettingsReader::Backends::YamlFile.new(Rails.root.join('config/settings.yml'))
|
39
|
+
]
|
40
|
+
# Configure resolvers.
|
41
|
+
config.resolvers = [
|
42
|
+
SettingsReader::Resolvers::Env.new,
|
43
|
+
SettingsReader::Resolvers::Erb.new
|
44
|
+
]
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
**NOTE** For rails you can add this code to as initializer `settings_reader.rb` in `app/config/initializers`
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
### Example settings structure
|
52
|
+
|
53
|
+
Assuming your defaults settings file in repository `config/settings.yml` looks like:
|
54
|
+
```yaml
|
55
|
+
my_cool_app:
|
56
|
+
app_name: 'MyCoolApp'
|
57
|
+
url: 'http://localhost:3001'
|
58
|
+
|
59
|
+
integrations:
|
60
|
+
database:
|
61
|
+
domain: localhost
|
62
|
+
user: app
|
63
|
+
password: password1234
|
64
|
+
parameters:
|
65
|
+
pool: 20
|
66
|
+
ssl: false
|
67
|
+
```
|
68
|
+
|
69
|
+
And production config `config/settings/produciton.yml` has following values
|
70
|
+
```yaml
|
71
|
+
my_cool_app:
|
72
|
+
url: 'https://mycoolapp.com'
|
73
|
+
|
74
|
+
integrations:
|
75
|
+
database:
|
76
|
+
domain: 10.0.5.141
|
77
|
+
password: 'env://DATABASE_PASSWORD'
|
78
|
+
```
|
79
|
+
|
80
|
+
### Get setting via full path
|
81
|
+
|
82
|
+
Anywhere in your code base, after initialization, you can use
|
83
|
+
previously loaded settings to query any key by full path
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
APP_SETTINGS['app_name'] # "MyCoolApp"
|
87
|
+
APP_SETTINGS.get(:hostname) # "https://mycoolapp.com"
|
88
|
+
|
89
|
+
APP_SETTINGS.get('integrations/database/user') # "app"
|
90
|
+
APP_SETTINGS['integrations/database/password'] # Value of environment variable DATABASE_PASS
|
91
|
+
|
92
|
+
#if you try to get sub settings via get - error is raised
|
93
|
+
APP_SETTINGS.get('integrations/database') # raise SettingsReader::Error
|
94
|
+
```
|
95
|
+
|
96
|
+
**IMPORTANT** If you try to get settings tree via `get` method `SettingsReader::Error` is going to be raised.
|
97
|
+
This is done due to the fact that we need to resolve settings every time they are requested.
|
98
|
+
Resolving whole tree upfront is not possible as gem is not aware about final structure of all backends
|
99
|
+
|
100
|
+
### Sub settings
|
101
|
+
|
102
|
+
Assuming some part of your code needs to work with smaller part of settings -
|
103
|
+
gem provides interface to avoid repeating absolute path
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
# You can load sub settings from root object
|
107
|
+
db_settings = APP_SETTINGS.load('integrations/database') # SettingsReader::Reader
|
108
|
+
db_settings.get(:domain) # "10.0.5.141"
|
109
|
+
db_settings['user'] # "app"
|
110
|
+
db_params = db_settings.load('parameters') # SettingsReader::Reader
|
111
|
+
```
|
112
|
+
|
113
|
+
## Advanced Configurations & Customization
|
114
|
+
|
115
|
+
### Backends
|
116
|
+
Backends controls how and in which order settings are retrieved.
|
117
|
+
During initial load - provide list of backend instances you want to query on all requests.
|
118
|
+
|
119
|
+
When application asks for specific setting - gem asks every backend in order of the configuration
|
120
|
+
until one returns not nil value. Full path to the setting provided to backend
|
121
|
+
|
122
|
+
Default order for providers is:
|
123
|
+
1. `SettingsReader::Backends::YamlFile.new('config/app_settings.local.yml')`
|
124
|
+
2. `SettingsReader::YamlFile.new('config/app_settings.yml')`
|
125
|
+
|
126
|
+
Additional backend plugins:
|
127
|
+
- [settings_reader-consul_backend]() - Implementation pending
|
128
|
+
|
129
|
+
Custom provider can be added as long as it support following interface:
|
130
|
+
```ruby
|
131
|
+
class CustomProvider
|
132
|
+
# get value by full_path or return nil if missing
|
133
|
+
def get(full_path)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
### Resolvers
|
139
|
+
Once value is retrieved - it will be additionally processed by resolvers.
|
140
|
+
This allows for additional flexibility like resolving one specific value in external sources.
|
141
|
+
|
142
|
+
While every resolver can be implemented in a form of a provider - one will be limited by the structure of settings,
|
143
|
+
that other system might not be compatible with this.
|
144
|
+
|
145
|
+
When value is retrieved - gem finds **first** provider that can resolve value and resolves it.
|
146
|
+
Resolved value is returned to application.
|
147
|
+
|
148
|
+
Default list of resolvers:
|
149
|
+
- `SettingsReader::Resolvers::Env.new`
|
150
|
+
- `SettingsReader::Resolvers::Erb.new`
|
151
|
+
|
152
|
+
List of built in resolvers:
|
153
|
+
- `SettingsReader::Resolvers::Env` - resolves compatible value by looking up environment variable.
|
154
|
+
Matching any value that starts with `env://`. Value like `env://TEST_URL` will be resolved as `ENV['TEST_URL']`
|
155
|
+
- `SettingsReader::Resolvers::Erb` - resolves value by rendering it via ERB if it contains ERB template.
|
156
|
+
Matching any value that contains `<%` and `%>` in it. Value like `<%= 2 + 2 %>` will be resolved as `4`
|
157
|
+
|
158
|
+
Additional resolver plugins:
|
159
|
+
- [settings_reader-vault_resolver](https://github.com/matic-insurance/settings_reader-vault_resolver) -
|
160
|
+
resolves compatible value by getting it from Vault. Matching any value that starts with `vault://`.
|
161
|
+
Value like `vault://secret/my_app/secrets#foo` will be resolved in vault as `Vault.kv('secret').get('my_app/secrets')`
|
162
|
+
and attribute `foo` will be retrieved from the resolved secret.
|
163
|
+
|
164
|
+
Custom resolver can be added as long as it support following interface:
|
165
|
+
```ruby
|
166
|
+
class CustomResolver
|
167
|
+
# should return true if current value should be resolved
|
168
|
+
def resolvable?(value, full_path)
|
169
|
+
end
|
170
|
+
|
171
|
+
# resolve value
|
172
|
+
def resolve(value, full_path)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
### Gem Configuration
|
178
|
+
|
179
|
+
You can configure gem while loading settings:
|
180
|
+
```ruby
|
181
|
+
APP_SETTINGS = SettingsReader.configure do |config|
|
182
|
+
config.backends = []
|
183
|
+
config.resolvers = []
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
### Default gem configuration
|
188
|
+
Below is current default gem configuration
|
189
|
+
```ruby
|
190
|
+
APP_SETTINGS = SettingsReader.load do |config|
|
191
|
+
config.backends = [
|
192
|
+
SettingsReader::Backends::YamlFile.new('config/app_settings.local.yml'),
|
193
|
+
SettingsReader::Backends::YamlFile.new('config/app_settings.yml')
|
194
|
+
]
|
195
|
+
config.resolvers = [
|
196
|
+
SettingsReader::Resolvers::Env.new,
|
197
|
+
SettingsReader::Resolvers::Erb.new
|
198
|
+
]
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
## Development
|
203
|
+
|
204
|
+
1. Run `bin/setup` to install dependencies
|
205
|
+
1. Run tests `rspec`
|
206
|
+
1. Add new test
|
207
|
+
1. Add new code
|
208
|
+
1. Go to step 3
|
209
|
+
1. Create PR
|
210
|
+
|
211
|
+
## Contributing
|
212
|
+
|
213
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/matic-insurance/settings_reader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/matic-insurance/settings_reader/blob/master/CODE_OF_CONDUCT.md).
|
214
|
+
|
215
|
+
|
216
|
+
## License
|
217
|
+
|
218
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
219
|
+
|
220
|
+
## Code of Conduct
|
221
|
+
|
222
|
+
Everyone interacting in the SettingsReader project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/matic-insurance/settings_reader/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'settings_reader'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Backends
|
3
|
+
# Abstract class with basic functionality
|
4
|
+
class Abstract
|
5
|
+
include SettingsReader::Mixins::Path
|
6
|
+
include SettingsReader::Mixins::Values
|
7
|
+
|
8
|
+
# get value from backend by full_path or return nil if missing
|
9
|
+
def get(_full_path)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SettingsReader
|
4
|
+
module Backends
|
5
|
+
# Provides access to settings stored as YAML file.
|
6
|
+
# File will be read once on init and cached in memory:
|
7
|
+
# When file is missing - no error will be raised
|
8
|
+
# When file is invalid - SettingsReader::Error is raised
|
9
|
+
class YamlFile < Abstract
|
10
|
+
def initialize(file_path)
|
11
|
+
super()
|
12
|
+
@data = read_yml(file_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(path)
|
16
|
+
parts = decompose_path(path)
|
17
|
+
get_value_from_hash(@data, parts)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def read_yml(path)
|
23
|
+
return {} unless File.exist?(path)
|
24
|
+
|
25
|
+
YAML.safe_load(IO.read(path))
|
26
|
+
rescue Psych::SyntaxError, Errno::ENOENT => e
|
27
|
+
raise SettingsReader::Error, "Cannot read settings file at #{path}: #{e.message}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
# All gem configuration settings
|
3
|
+
class Configuration
|
4
|
+
DEFAULT_BASE_FILE_PATH = 'config/app_settings.yml'.freeze
|
5
|
+
DEFAULT_LOCAL_FILE_PATH = 'config/app_settings.local.yml'.freeze
|
6
|
+
attr_accessor :backends, :resolvers
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@base_file_path = DEFAULT_BASE_FILE_PATH
|
10
|
+
@local_file_path = DEFAULT_LOCAL_FILE_PATH
|
11
|
+
@backends = [
|
12
|
+
SettingsReader::Backends::YamlFile.new(DEFAULT_LOCAL_FILE_PATH),
|
13
|
+
SettingsReader::Backends::YamlFile.new(DEFAULT_BASE_FILE_PATH)
|
14
|
+
]
|
15
|
+
@resolvers = [
|
16
|
+
SettingsReader::Resolvers::Env.new
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Mixins
|
3
|
+
# Path related utility methods
|
4
|
+
module Path
|
5
|
+
PATH_SEPARATOR = '/'.freeze
|
6
|
+
|
7
|
+
# Create path from parts using '/' as a separator
|
8
|
+
def generate_path(*parts)
|
9
|
+
strings = parts.map(&:to_s)
|
10
|
+
all_parts = strings.map { |s| s.split(PATH_SEPARATOR) }.flatten
|
11
|
+
all_parts.reject(&:empty?).join('/')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create list of parts from path using '/' as separator. Remove empty/nil parts
|
15
|
+
def decompose_path(path)
|
16
|
+
parts = path.to_s.split(PATH_SEPARATOR).compact
|
17
|
+
parts.reject(&:empty?)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Mixins
|
3
|
+
# Value casting utility methods
|
4
|
+
module Values
|
5
|
+
PARSING_CLASSES = [Integer, Float, ->(value) { JSON.parse(value) }].freeze
|
6
|
+
|
7
|
+
def cast_value_from_string(value)
|
8
|
+
return nil if value.nil?
|
9
|
+
return false if value == 'false'
|
10
|
+
return true if value == 'true'
|
11
|
+
return value if value.is_a?(Hash)
|
12
|
+
return convert_to_hash(value) if value.is_a?(Array)
|
13
|
+
|
14
|
+
cast_complex_value(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_value_from_hash(data, path_parts)
|
18
|
+
data.dig(*path_parts).clone
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def cast_complex_value(value)
|
24
|
+
PARSING_CLASSES.each do |parser|
|
25
|
+
return parser.call(value)
|
26
|
+
rescue StandardError => _e
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
value.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_to_hash(data)
|
33
|
+
data_h = data.map do |item|
|
34
|
+
value = cast_value_from_string(item[:value])
|
35
|
+
item[:key].split('/').reverse.reduce(value) { |h, v| { v => h } }
|
36
|
+
end
|
37
|
+
data_h.reduce({}) do |dest, source|
|
38
|
+
source.merge(dest)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
# Orchestrates fetching values from backend and resolving them
|
3
|
+
class Reader
|
4
|
+
include SettingsReader::Mixins::Path
|
5
|
+
|
6
|
+
def initialize(base_path, config)
|
7
|
+
@base_path = base_path
|
8
|
+
@config = config
|
9
|
+
@backends = config.backends
|
10
|
+
@resolvers = config.resolvers
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(sub_path)
|
14
|
+
full_path = generate_path(@base_path, sub_path)
|
15
|
+
value = fetch_value(full_path)
|
16
|
+
resolve_value(value, full_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
alias [] get
|
20
|
+
|
21
|
+
def load(sub_path)
|
22
|
+
new_path = generate_path(@base_path, sub_path)
|
23
|
+
SettingsReader::Reader.new(new_path, @config)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def check_deep_structure(value, path)
|
29
|
+
return unless value.is_a?(Hash)
|
30
|
+
|
31
|
+
message = "Getting value of complex object at path: '#{path}'. Use #load method to get new scoped instance"
|
32
|
+
raise SettingsReader::Error, message if value.is_a?(Hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
def fetch_value(path)
|
36
|
+
@backends.each do |backend|
|
37
|
+
value = backend.get(path)
|
38
|
+
check_deep_structure(value, path)
|
39
|
+
return value unless value.nil?
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def resolve_value(value, path)
|
45
|
+
resolver = @resolvers.detect { |r| r.resolvable?(value, path) }
|
46
|
+
if resolver
|
47
|
+
resolver.resolve(value, path)
|
48
|
+
else
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Resolvers
|
3
|
+
# Abstract resolver interface
|
4
|
+
class Abstract
|
5
|
+
include SettingsReader::Mixins::Values
|
6
|
+
|
7
|
+
# Should return true if value is resolvable
|
8
|
+
def resolvable?(_value, _path)
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
# Should resolve value
|
13
|
+
def resolve(value, _path)
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Resolvers
|
3
|
+
# Resolve values in environment variable
|
4
|
+
class Env
|
5
|
+
IDENTIFIER = 'env://'.freeze
|
6
|
+
|
7
|
+
# Returns true when value starts from `env://`
|
8
|
+
def resolvable?(value, _path)
|
9
|
+
return unless value.respond_to?(:start_with?)
|
10
|
+
|
11
|
+
value.start_with?(IDENTIFIER)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return value of environment variable by removing `env://` prefix and calling `ENV[env_path]`
|
15
|
+
def resolve(value, _path)
|
16
|
+
env_path = value.to_s.delete_prefix(IDENTIFIER)
|
17
|
+
ENV[env_path]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SettingsReader
|
2
|
+
module Resolvers
|
3
|
+
# Run values through ERB
|
4
|
+
class Erb
|
5
|
+
IDENTIFIER = /(<%=).*(%>)/.freeze
|
6
|
+
|
7
|
+
# Returns true when value contain Erb template <%= code_is_here %>
|
8
|
+
def resolvable?(value, _path)
|
9
|
+
return unless value.is_a?(String)
|
10
|
+
|
11
|
+
IDENTIFIER.match?(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Renders value using ERB
|
15
|
+
def resolve(value, _path)
|
16
|
+
ERB.new(value.to_s).result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'settings_reader/mixins/path'
|
2
|
+
require 'settings_reader/mixins/values'
|
3
|
+
require 'settings_reader/backends/abstract'
|
4
|
+
require 'settings_reader/backends/yaml_file'
|
5
|
+
require 'settings_reader/resolvers/abstract'
|
6
|
+
require 'settings_reader/resolvers/env'
|
7
|
+
require 'settings_reader/resolvers/erb'
|
8
|
+
require 'settings_reader/configuration'
|
9
|
+
require 'settings_reader/reader'
|
10
|
+
require 'settings_reader/version'
|
11
|
+
|
12
|
+
# Flexible Settings reader with support of custom backends and value resolutions
|
13
|
+
module SettingsReader
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
def self.load(base_path = '')
|
17
|
+
configuration = SettingsReader::Configuration.new
|
18
|
+
yield(configuration) if block_given?
|
19
|
+
Reader.new(base_path, configuration)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'lib/settings_reader/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'settings_reader'
|
5
|
+
spec.version = SettingsReader::VERSION
|
6
|
+
spec.authors = ['Volodymyr Mykhailyk']
|
7
|
+
spec.email = ['712680+volodymyr-mykhailyk@users.noreply.github.com']
|
8
|
+
|
9
|
+
spec.summary = 'Flexible Settings reader with support of custom backends and value resolutions'
|
10
|
+
spec.description = <<-DESCRIPTION
|
11
|
+
Customizable 2 step setting reading for your application.
|
12
|
+
|
13
|
+
First settings is retrieved from list of backends and afterwards processed
|
14
|
+
by the list of resolvers to allow even more flexibility.
|
15
|
+
DESCRIPTION
|
16
|
+
spec.homepage = 'https://github.com/matic-insurance/settings_reader'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
19
|
+
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
22
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
+
end
|
29
|
+
spec.bindir = 'exe'
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
|
33
|
+
spec.add_development_dependency 'codecov', '~> 0.4'
|
34
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
35
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
36
|
+
spec.add_development_dependency 'rubocop', '~> 0.66'
|
37
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.32.0'
|
38
|
+
spec.add_development_dependency 'simplecov', '~> 0.16'
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: settings_reader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Volodymyr Mykhailyk
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: codecov
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.66'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.66'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.32.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.32.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.16'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.16'
|
97
|
+
description: " Customizable 2 step setting reading for your application.\n\n First
|
98
|
+
settings is retrieved from list of backends and afterwards processed \n by the
|
99
|
+
list of resolvers to allow even more flexibility.\n"
|
100
|
+
email:
|
101
|
+
- 712680+volodymyr-mykhailyk@users.noreply.github.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".github/workflows/linters.yml"
|
107
|
+
- ".github/workflows/main.yml"
|
108
|
+
- ".gitignore"
|
109
|
+
- ".rspec"
|
110
|
+
- ".rubocop.yml"
|
111
|
+
- ".simplecov"
|
112
|
+
- ".travis.yml"
|
113
|
+
- CHANGELOG.md
|
114
|
+
- CODE_OF_CONDUCT.md
|
115
|
+
- Gemfile
|
116
|
+
- Gemfile.lock
|
117
|
+
- LICENSE.txt
|
118
|
+
- README.md
|
119
|
+
- Rakefile
|
120
|
+
- bin/console
|
121
|
+
- bin/setup
|
122
|
+
- lib/settings_reader.rb
|
123
|
+
- lib/settings_reader/backends/abstract.rb
|
124
|
+
- lib/settings_reader/backends/yaml_file.rb
|
125
|
+
- lib/settings_reader/configuration.rb
|
126
|
+
- lib/settings_reader/mixins/path.rb
|
127
|
+
- lib/settings_reader/mixins/values.rb
|
128
|
+
- lib/settings_reader/reader.rb
|
129
|
+
- lib/settings_reader/resolvers/abstract.rb
|
130
|
+
- lib/settings_reader/resolvers/env.rb
|
131
|
+
- lib/settings_reader/resolvers/erb.rb
|
132
|
+
- lib/settings_reader/version.rb
|
133
|
+
- settings_reader.gemspec
|
134
|
+
homepage: https://github.com/matic-insurance/settings_reader
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata:
|
138
|
+
homepage_uri: https://github.com/matic-insurance/settings_reader
|
139
|
+
source_code_uri: https://github.com/matic-insurance/settings_reader
|
140
|
+
changelog_uri: https://github.com/matic-insurance/settings_reader/blob/main/CHANGELOG.md
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 2.5.0
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubygems_version: 3.1.6
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Flexible Settings reader with support of custom backends and value resolutions
|
160
|
+
test_files: []
|