process_settings 0.4.0.pre.9 → 0.4.0.pre.10
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 +4 -4
- data/README.md +140 -14
- data/bin/combine_process_settings +4 -3
- data/lib/process_settings/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 605629bf6a5f9a31b9d46ea7b1d4ed83dc822b20
|
4
|
+
data.tar.gz: b6df3ada5eddb07783014fa9db62d02953e84238
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ce692cc7a87d7a2c3fa8b0b8b9f42a6f37c32bc1e4871bd509a27e544272e5957e395463dc99b39b3efcd9456be8aeacee05e9559f0e9d0a227af9d222294eb
|
7
|
+
data.tar.gz: '02548c651f62448c6ed659abba25227318164fbbd327bdc1d55f01731aa5c4fe3327f1e63ae165832b4a859be564913d6043db46e06a93e168cbb6d5dbc0c9b5'
|
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
# ProcessSettings
|
2
|
-
This gem provides dynamic settings for
|
3
|
-
|
4
|
-
|
1
|
+
# ProcessSettings
|
2
|
+
This gem provides dynamic settings for Ruby processes. The settings are stored in YAML.
|
3
|
+
Settings are managed in a git repo, in separate YAML files for each concern (for example, each micro-service). Each YAML file can be targeted based on matching context values (for example, `service_name`).
|
4
|
+
|
5
|
+
|
6
|
+
The context can be either static to the process (for example, `service_name` or `data_center`) or dynamic (for example, the current web request `domain`).
|
5
7
|
|
6
8
|
## Installation
|
7
9
|
To install this gem directly on your machine from rubygems, run the following:
|
@@ -11,27 +13,151 @@ gem install process_settings
|
|
11
13
|
|
12
14
|
To install this gem in your bundler project, add the following to your Gemfile:
|
13
15
|
```ruby
|
14
|
-
gem 'process_settings', '~> 0.
|
15
|
-
```
|
16
|
-
|
17
|
-
To use an unreleased version, add it to your Gemfile for Bundler:
|
18
|
-
```ruby
|
19
|
-
gem 'process_settings', git: 'git@github.com:Invoca/process_settings'
|
16
|
+
gem 'process_settings', '~> 0.4'
|
20
17
|
```
|
21
18
|
|
22
19
|
## Usage
|
23
|
-
|
24
|
-
|
20
|
+
The `ProcessSettings::Monitor` and related classes can be freely created and used at any time.
|
21
|
+
But typical usage is through the `ProcessSettings::Monitor.instance`.
|
22
|
+
That should be configured at process startup before use.
|
23
|
+
### Configuration
|
24
|
+
Before using `ProcessSettings::Monitor.instance`, you must first configure the path to the combined process settings file on disk,
|
25
|
+
and provide a logger.
|
25
26
|
```ruby
|
26
27
|
require 'process_settings'
|
28
|
+
|
29
|
+
ProcessSettings::Monitor.file_path = "/etc/process_settings/combined_process_settings.yml"
|
30
|
+
ProcessSettings::Monitor.logger = logger
|
31
|
+
```
|
32
|
+
### Monitor Initialization
|
33
|
+
The `ProcessSettings::Monitor` is a hybrid singleton. The class attribute `instance` returns
|
34
|
+
the current instance. If not already set, this is lazy-created based on the above configuration.
|
35
|
+
|
36
|
+
The monitor should be initialized with static (unchanging) context for your process:
|
37
|
+
```
|
38
|
+
ProcessSettings::Monitor.static_context = {
|
39
|
+
"service_name" => "frontend",
|
40
|
+
"data_center" => "AWS-US-EAST-1"
|
41
|
+
}
|
42
|
+
```
|
43
|
+
The `static_context` is important because it is used to pre-filter settings for the process.
|
44
|
+
For example, a setting that is targeted to `service_name: frontend` will match the above static context and
|
45
|
+
be simplified to `true`. In other processes with a different `service_name`, such a targeted setting will be
|
46
|
+
simplified to `false` and removed from memory.
|
47
|
+
|
48
|
+
Note that the `static_context` as well as `dynamic_context` must use strings, not symbols, for both keys and values.
|
49
|
+
|
50
|
+
### Reading Settings
|
51
|
+
For the following section, consider this `combined_process_settings.yml` file:
|
52
|
+
```
|
53
|
+
---
|
54
|
+
- filename: frontend.yml
|
55
|
+
settings:
|
56
|
+
frontend:
|
57
|
+
log_level: info
|
58
|
+
- filename: frontend-microsite.yml
|
59
|
+
target:
|
60
|
+
domain: microsite.example.com
|
61
|
+
settings:
|
62
|
+
frontend:
|
63
|
+
log_level: debug
|
64
|
+
- meta:
|
65
|
+
version: 27
|
66
|
+
END: true
|
67
|
+
```
|
68
|
+
|
69
|
+
To read a setting, application code should call the `[]` method on the `ProcessSettings` class. For example:
|
70
|
+
```
|
71
|
+
log_level = ProcessSettings['frontend', 'log_level']
|
72
|
+
=> "info"
|
73
|
+
```
|
74
|
+
#### ProcessSettings[] interface
|
75
|
+
The `ProcessSettings[]` method delegates to `ProcessSettings::Monitor#[]` on the `instance`.
|
76
|
+
|
77
|
+
`[]` interface:
|
78
|
+
|
79
|
+
```
|
80
|
+
[](*path, dynamic_context: {}, required: true)
|
81
|
+
```
|
82
|
+
|
83
|
+
|argument|description|
|
84
|
+
|--------|-------------|
|
85
|
+
|_path_ |A series of 1 or more comma-separated strings forming a path to navigate the `settings` hash, starting at the top.|
|
86
|
+
|`dynamic_context:` |An optional hash of dynamic settings, used to target the settings. This will automatically be deep-merged with the static context. It may not contradict the static context. |
|
87
|
+
|`required:` |A boolean indicating if the setting is required to be present. If a setting is missing, then if `required` is truthy, a `ProcesssSettings::SettingsPathNotFound` exception will be raised. Otherwise, `nil` will be returned. Default: `true`.
|
88
|
+
|
89
|
+
Example with `dynamic_context`:
|
90
|
+
```
|
91
|
+
log_level = ProcessSettings['frontend', 'log_level',
|
92
|
+
dynamic_context: { "domain" => "microsite.example.com" }
|
93
|
+
]
|
94
|
+
=> "debug"
|
27
95
|
```
|
28
96
|
|
29
|
-
|
97
|
+
Example with `required: true` (default) that was not found:
|
98
|
+
```
|
99
|
+
http_version = ProcessSettings['frontend', 'http_version']
|
100
|
+
|
101
|
+
exception raised!
|
102
|
+
|
103
|
+
ProcessSettings::SettingsPathNotFound: No settings found for path ["frontend", "http_version"]
|
104
|
+
```
|
105
|
+
|
106
|
+
Here is the same example with `required: false`, applying a default value of `2`:
|
107
|
+
```
|
108
|
+
http_version = ProcessSettings['frontend', 'http_version', required: false] || 2
|
109
|
+
```
|
30
110
|
|
31
111
|
### Dynamic Settings
|
32
112
|
|
33
|
-
|
113
|
+
The `ProcessSettings::Monitor` loads settings changes dynamically whenever the file changes,
|
114
|
+
by using the [listen](https://github.com/guard/listen) gem which in turn uses the `INotify` module of the Linux kernel, or `FSEvents` on MacOS. There is no need to restart the process or send it a signal to tell it to reload changes.
|
115
|
+
|
116
|
+
There are two ways to get access the latest settings from inside the process:
|
117
|
+
|
118
|
+
#### Read Latest Setting Through `ProcessSettings[]`
|
119
|
+
|
120
|
+
The simplest approach--as shown above--is to read the latest settings at any time through `ProcessSettings[]` (which delegates to `ProcessSettings::Monitor.instance`):
|
121
|
+
```
|
122
|
+
http_version = ProcessSettings['frontend', 'http_version']
|
123
|
+
```
|
124
|
+
|
125
|
+
#### Register an `on_change` Callback
|
126
|
+
Alternatively, if you need to execute some code when there is a change, register a callback with `ProcessSettings::Monitor#on_change`:
|
127
|
+
```
|
128
|
+
ProcessSettings::Monitor.instance.on_change do
|
129
|
+
logger.level = ProcessSettings['frontend', 'log_level']
|
130
|
+
end
|
131
|
+
```
|
132
|
+
Note that all callbacks run sequentially on the shared change monitoring thread, so please be considerate!
|
133
|
+
|
134
|
+
There is no provision for unregistering callbacks. Instead, replace the `instance` of the monitor with a new one.
|
135
|
+
|
136
|
+
## Targeting
|
137
|
+
Each settings YAML file has an optional `target` key at the top level, next to `settings`.
|
138
|
+
|
139
|
+
If there is no `target` key, the target defaults to `true`, meaning all processes are targeted for these settings. (However, the settings may be overridden by other YAML files. See "Precedence" below.)
|
140
|
+
|
141
|
+
### Hash Key-Values Are AND'd
|
142
|
+
To `target` on context values, provide a hash of key-value pairs. All keys must match for the target to be met. For example, consider this target hash:
|
143
|
+
```
|
144
|
+
target:
|
145
|
+
service_name: frontend
|
146
|
+
data_center: AWS-US-EAST-1
|
147
|
+
```
|
148
|
+
This will be applied in any process that has `service_name == "frontend"` AND is running in `data_center == "AWS-US-EAST-1"`.
|
149
|
+
|
150
|
+
### Multiple Values Are OR'd
|
151
|
+
Values may be set to an array, in which case the key matches if _any_ of the values matches. For example, consider this target hash:
|
152
|
+
```
|
153
|
+
target:
|
154
|
+
service_name: [frontend, auth]
|
155
|
+
data_center: AWS-US-EAST-1
|
156
|
+
```
|
157
|
+
This will be applied in any process that has (`service_name == "frontend"` OR `service_name == "auth"`) AND `data_center == "AWS-US-EAST-1"`.
|
34
158
|
|
159
|
+
### Precedence
|
160
|
+
The settings YAML files are always combined in alphabetical order by file path. Later settings take precedence over the earlier ones.
|
35
161
|
|
36
162
|
## Contributions
|
37
163
|
|
@@ -32,15 +32,16 @@ def parse_options(argv)
|
|
32
32
|
options.verbose = false
|
33
33
|
option_parser = OptionParser.new(argv) do |opt|
|
34
34
|
opt.on('-v', '--verbose', 'Verbose mode.') { options.verbose = true }
|
35
|
+
opt.on('-n', '--version=VERSION', 'Set version number.') { |value| options.version = value.to_i }
|
35
36
|
opt.on('-r', '--root_folder=ROOT') { |o| options.root_folder = o }
|
36
37
|
opt.on('-o', '--output=FILENAME', 'Output file.') { |o| options.output_filename = o }
|
37
38
|
opt.on('-i', '--initial=FILENAME', 'Initial settings file for version inference.') { |o| options.initial_filename = o }
|
38
39
|
end
|
39
40
|
|
40
|
-
if option_parser.parse! && options.root_folder && options.output_filename && (
|
41
|
+
if option_parser.parse! && options.root_folder && options.output_filename && (options.version || options.initial_filename)
|
41
42
|
options
|
42
43
|
else
|
43
|
-
warn "usage: #{PROGRAM_NAME} -r staging|production -o combined_process_settings.yml [-i initial_combined_process_settings.yml] (required if
|
44
|
+
warn "usage: #{PROGRAM_NAME} -r staging|production -o combined_process_settings.yml [--version=VERSION] [-i initial_combined_process_settings.yml] (-i required if --version= not set)"
|
44
45
|
option_parser.summarize(STDERR)
|
45
46
|
exit(1)
|
46
47
|
end
|
@@ -82,7 +83,7 @@ options = parse_options(ARGV.dup)
|
|
82
83
|
|
83
84
|
combined_settings = read_and_combine_settings(Pathname.new(options.root_folder) + SETTINGS_FOLDER)
|
84
85
|
|
85
|
-
version_number =
|
86
|
+
version_number = options.version || default_version_number(options.initial_filename)
|
86
87
|
combined_settings << end_marker(version_number)
|
87
88
|
|
88
89
|
yaml = combined_settings.to_yaml
|