myway_config 0.1.1
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/.envrc +1 -0
- data/CHANGELOG.md +5 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +235 -0
- data/Rakefile +8 -0
- data/docs/api/base.md +292 -0
- data/docs/api/config-section.md +368 -0
- data/docs/api/index.md +85 -0
- data/docs/api/loaders.md +296 -0
- data/docs/development/contributing.md +215 -0
- data/docs/development/index.md +100 -0
- data/docs/development/testing.md +276 -0
- data/docs/examples/basic-usage.md +173 -0
- data/docs/examples/index.md +101 -0
- data/docs/examples/rails-integration.md +302 -0
- data/docs/examples/standalone-app.md +278 -0
- data/docs/getting-started/index.md +23 -0
- data/docs/getting-started/installation.md +50 -0
- data/docs/getting-started/quick-start.md +109 -0
- data/docs/guides/accessing-values.md +153 -0
- data/docs/guides/custom-file-loading.md +161 -0
- data/docs/guides/defining-configuration.md +133 -0
- data/docs/guides/environment-overrides.md +137 -0
- data/docs/guides/hash-like-behavior.md +220 -0
- data/docs/guides/index.md +15 -0
- data/docs/guides/yaml-structure.md +168 -0
- data/docs/index.md +86 -0
- data/lib/myway_config/base.rb +304 -0
- data/lib/myway_config/config_section.rb +168 -0
- data/lib/myway_config/loaders/defaults_loader.rb +178 -0
- data/lib/myway_config/loaders/xdg_config_loader.rb +120 -0
- data/lib/myway_config/version.rb +5 -0
- data/lib/myway_config.rb +66 -0
- data/mkdocs.yml +148 -0
- data/sig/myway_config.rbs +4 -0
- metadata +96 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Hash-like Behavior
|
|
2
|
+
|
|
3
|
+
ConfigSection provides Hash-like access and Enumerable support.
|
|
4
|
+
|
|
5
|
+
## What is ConfigSection?
|
|
6
|
+
|
|
7
|
+
When you define a nested configuration in YAML:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
defaults:
|
|
11
|
+
database:
|
|
12
|
+
host: localhost
|
|
13
|
+
port: 5432
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The `database` value becomes a `ConfigSection` object, not a plain Hash:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
config.database.class # => MywayConfig::ConfigSection
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Why ConfigSection?
|
|
23
|
+
|
|
24
|
+
ConfigSection provides:
|
|
25
|
+
|
|
26
|
+
1. **Method access** - `config.database.host` instead of `config.database[:host]`
|
|
27
|
+
2. **Multiple access patterns** - Methods, symbols, and strings all work
|
|
28
|
+
3. **Hash-like enumeration** - `Enumerable` methods like `map`, `select`, `find`
|
|
29
|
+
4. **Safe nested access** - `dig` for deep traversal
|
|
30
|
+
|
|
31
|
+
## Access Methods
|
|
32
|
+
|
|
33
|
+
### Method Syntax
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
config.database.host # => "localhost"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Bracket Syntax
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
config.database[:host] # => "localhost"
|
|
43
|
+
config.database["host"] # => "localhost"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Setting Values
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
config.database.host = "new-host"
|
|
50
|
+
config.database[:port] = 5433
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Hash Methods
|
|
54
|
+
|
|
55
|
+
### Keys and Values
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
config.database.keys # => [:host, :port, :name, :pool]
|
|
59
|
+
config.database.values # => ["localhost", 5432, "myapp_db", 5]
|
|
60
|
+
config.database.size # => 4
|
|
61
|
+
config.database.length # => 4
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Key Checking
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
config.database.key?(:host) # => true
|
|
68
|
+
config.database.has_key?(:host) # => true
|
|
69
|
+
config.database.include?(:host) # => true
|
|
70
|
+
config.database.member?(:host) # => true
|
|
71
|
+
|
|
72
|
+
config.database.empty? # => false
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Fetch
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
# With key
|
|
79
|
+
config.database.fetch(:host) # => "localhost"
|
|
80
|
+
|
|
81
|
+
# With default
|
|
82
|
+
config.database.fetch(:missing, "default") # => "default"
|
|
83
|
+
|
|
84
|
+
# With block
|
|
85
|
+
config.database.fetch(:missing) { |k| "no #{k}" } # => "no missing"
|
|
86
|
+
|
|
87
|
+
# Without default (raises KeyError)
|
|
88
|
+
config.database.fetch(:missing) # => KeyError: key not found: :missing
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Dig
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
config.api.dig(:headers, :content_type) # => "application/json"
|
|
95
|
+
config.api.dig(:missing, :nested) # => nil
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Enumerable Methods
|
|
99
|
+
|
|
100
|
+
ConfigSection includes `Enumerable`:
|
|
101
|
+
|
|
102
|
+
### each
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
config.database.each do |key, value|
|
|
106
|
+
puts "#{key}: #{value}"
|
|
107
|
+
end
|
|
108
|
+
# host: localhost
|
|
109
|
+
# port: 5432
|
|
110
|
+
# ...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### map
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
config.database.map { |k, v| "#{k}=#{v}" }
|
|
117
|
+
# => ["host=localhost", "port=5432", ...]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### select / reject
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# Select numeric values
|
|
124
|
+
config.database.select { |k, v| v.is_a?(Integer) }
|
|
125
|
+
# => [[:port, 5432], [:pool, 5]]
|
|
126
|
+
|
|
127
|
+
# Reject nil values
|
|
128
|
+
config.database.reject { |k, v| v.nil? }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### find / detect
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
config.database.find { |k, v| v == 5432 }
|
|
135
|
+
# => [:port, 5432]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### any? / all? / none?
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
config.database.any? { |k, v| v.nil? } # => false
|
|
142
|
+
config.database.all? { |k, v| v } # => true
|
|
143
|
+
config.database.none? { |k, v| v.nil? } # => true
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Other Enumerable Methods
|
|
147
|
+
|
|
148
|
+
All standard Enumerable methods work:
|
|
149
|
+
|
|
150
|
+
- `count`, `first`, `take`, `drop`
|
|
151
|
+
- `min`, `max`, `minmax`
|
|
152
|
+
- `sort`, `sort_by`
|
|
153
|
+
- `group_by`, `partition`
|
|
154
|
+
- `reduce`, `inject`
|
|
155
|
+
- And more...
|
|
156
|
+
|
|
157
|
+
## Conversion
|
|
158
|
+
|
|
159
|
+
### to_h
|
|
160
|
+
|
|
161
|
+
Convert to a plain Ruby Hash:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
config.database.to_h
|
|
165
|
+
# => {host: "localhost", port: 5432, name: "myapp_db", pool: 5}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Nested ConfigSections are also converted:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
config.api.to_h
|
|
172
|
+
# => {
|
|
173
|
+
# base_url: "https://api.example.com",
|
|
174
|
+
# timeout: 30,
|
|
175
|
+
# headers: {content_type: "application/json"}
|
|
176
|
+
# }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### merge
|
|
180
|
+
|
|
181
|
+
Merge with another ConfigSection or Hash:
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
overrides = { host: "new-host", pool: 10 }
|
|
185
|
+
merged = config.database.merge(overrides)
|
|
186
|
+
|
|
187
|
+
merged.host # => "new-host"
|
|
188
|
+
merged.pool # => 10
|
|
189
|
+
merged.port # => 5432 (from original)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Practical Examples
|
|
193
|
+
|
|
194
|
+
### Building Connection Strings
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
db = config.database
|
|
198
|
+
connection_string = "postgres://#{db.host}:#{db.port}/#{db.name}"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Filtering Configuration
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
# Get all non-nil database settings
|
|
205
|
+
config.database.reject { |k, v| v.nil? }.to_h
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Transforming Values
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
# Upcase all string values
|
|
212
|
+
config.database.map { |k, v|
|
|
213
|
+
[k, v.is_a?(String) ? v.upcase : v]
|
|
214
|
+
}.to_h
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Next Steps
|
|
218
|
+
|
|
219
|
+
- [Examples](../examples/index.md) - Real-world usage examples
|
|
220
|
+
- [API Reference](../api/config-section.md) - Complete ConfigSection API
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Guides
|
|
2
|
+
|
|
3
|
+
These guides cover MywayConfig features in depth.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
- [Defining Configuration](defining-configuration.md) - How to create config classes
|
|
8
|
+
- [YAML Structure](yaml-structure.md) - Understanding the defaults file format
|
|
9
|
+
- [Accessing Values](accessing-values.md) - All the ways to read configuration
|
|
10
|
+
|
|
11
|
+
## Advanced Topics
|
|
12
|
+
|
|
13
|
+
- [Environment Overrides](environment-overrides.md) - Using environment variables
|
|
14
|
+
- [Custom File Loading](custom-file-loading.md) - Loading from non-standard locations
|
|
15
|
+
- [Hash-like Behavior](hash-like-behavior.md) - Using ConfigSection as a Hash
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# YAML Structure
|
|
2
|
+
|
|
3
|
+
The YAML defaults file is the single source of truth for your configuration structure and default values.
|
|
4
|
+
|
|
5
|
+
## Required Structure
|
|
6
|
+
|
|
7
|
+
The file must have a `defaults` key. Other top-level keys are environment names.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
defaults: # Required - defines structure and default values
|
|
11
|
+
key: value
|
|
12
|
+
|
|
13
|
+
development: # Optional - overrides for development
|
|
14
|
+
key: dev_value
|
|
15
|
+
|
|
16
|
+
production: # Optional - overrides for production
|
|
17
|
+
key: prod_value
|
|
18
|
+
|
|
19
|
+
test: # Optional - overrides for test
|
|
20
|
+
key: test_value
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## The `defaults` Section
|
|
24
|
+
|
|
25
|
+
The `defaults` section defines:
|
|
26
|
+
|
|
27
|
+
1. **Structure** - What keys exist in your configuration
|
|
28
|
+
2. **Types** - Hash values become `ConfigSection`, symbols stay symbols
|
|
29
|
+
3. **Default values** - Base values before any overrides
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
defaults:
|
|
33
|
+
# Nested configuration (becomes ConfigSection)
|
|
34
|
+
database:
|
|
35
|
+
host: localhost
|
|
36
|
+
port: 5432
|
|
37
|
+
name: myapp_db
|
|
38
|
+
pool: 5
|
|
39
|
+
|
|
40
|
+
# Another nested section
|
|
41
|
+
api:
|
|
42
|
+
base_url: https://api.example.com
|
|
43
|
+
timeout: 30
|
|
44
|
+
headers:
|
|
45
|
+
content_type: application/json
|
|
46
|
+
|
|
47
|
+
# Symbol value (coerced to :info)
|
|
48
|
+
log_level: :info
|
|
49
|
+
|
|
50
|
+
# Scalar values
|
|
51
|
+
debug: false
|
|
52
|
+
max_connections: 10
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Environment Sections
|
|
56
|
+
|
|
57
|
+
Environment sections contain only overrides. Values merge with defaults.
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
defaults:
|
|
61
|
+
database:
|
|
62
|
+
host: localhost
|
|
63
|
+
port: 5432
|
|
64
|
+
name: myapp_db
|
|
65
|
+
log_level: :info
|
|
66
|
+
debug: false
|
|
67
|
+
|
|
68
|
+
development:
|
|
69
|
+
# Only override what's different
|
|
70
|
+
database:
|
|
71
|
+
name: myapp_development
|
|
72
|
+
log_level: :debug
|
|
73
|
+
debug: true
|
|
74
|
+
|
|
75
|
+
production:
|
|
76
|
+
database:
|
|
77
|
+
host: prod-db.example.com
|
|
78
|
+
name: myapp_production
|
|
79
|
+
pool: 25
|
|
80
|
+
log_level: :warn
|
|
81
|
+
# debug remains false (from defaults)
|
|
82
|
+
|
|
83
|
+
test:
|
|
84
|
+
database:
|
|
85
|
+
name: myapp_test
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Merge Behavior
|
|
89
|
+
|
|
90
|
+
Environment values are deep-merged with defaults:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
defaults:
|
|
94
|
+
database:
|
|
95
|
+
host: localhost
|
|
96
|
+
port: 5432
|
|
97
|
+
name: myapp_db
|
|
98
|
+
pool: 5
|
|
99
|
+
|
|
100
|
+
production:
|
|
101
|
+
database:
|
|
102
|
+
host: prod-db.example.com
|
|
103
|
+
pool: 25
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Result in production:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
config.database.host # => "prod-db.example.com" (overridden)
|
|
110
|
+
config.database.port # => 5432 (from defaults)
|
|
111
|
+
config.database.name # => "myapp_db" (from defaults)
|
|
112
|
+
config.database.pool # => 25 (overridden)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Supported Value Types
|
|
116
|
+
|
|
117
|
+
| YAML Type | Ruby Type | Notes |
|
|
118
|
+
|-----------|-----------|-------|
|
|
119
|
+
| `string` | `String` | Plain strings |
|
|
120
|
+
| `123` | `Integer` | Numbers |
|
|
121
|
+
| `1.5` | `Float` | Decimals |
|
|
122
|
+
| `true`/`false` | `Boolean` | Booleans |
|
|
123
|
+
| `:symbol` | `Symbol` | Colon prefix for symbols |
|
|
124
|
+
| `hash:` | `ConfigSection` | Nested hashes become ConfigSection |
|
|
125
|
+
| `[a, b]` | `Array` | Arrays |
|
|
126
|
+
| `null` | `nil` | Null values |
|
|
127
|
+
|
|
128
|
+
## Environment Detection
|
|
129
|
+
|
|
130
|
+
The current environment is determined by (in order):
|
|
131
|
+
|
|
132
|
+
1. `Anyway::Settings.current_environment`
|
|
133
|
+
2. `ENV['RAILS_ENV']`
|
|
134
|
+
3. `ENV['RACK_ENV']`
|
|
135
|
+
4. `'development'` (default)
|
|
136
|
+
|
|
137
|
+
## Custom Environments
|
|
138
|
+
|
|
139
|
+
You can define any environment name:
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
defaults:
|
|
143
|
+
api_url: https://api.example.com
|
|
144
|
+
|
|
145
|
+
staging:
|
|
146
|
+
api_url: https://staging-api.example.com
|
|
147
|
+
|
|
148
|
+
canary:
|
|
149
|
+
api_url: https://canary-api.example.com
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
RACK_ENV=staging ruby app.rb
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Valid Environments
|
|
157
|
+
|
|
158
|
+
You can check if an environment is defined:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
MyApp::Config.valid_environments # => [:development, :production, :test]
|
|
162
|
+
MyApp::Config.valid_environment? # => true (if current env is defined)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Next Steps
|
|
166
|
+
|
|
167
|
+
- [Accessing Values](accessing-values.md) - How to read your configuration
|
|
168
|
+
- [Environment Overrides](environment-overrides.md) - Override with environment variables
|
data/docs/index.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# MywayConfig
|
|
2
|
+
|
|
3
|
+
**Configuration management for Ruby applications with XDG support and auto-configuration from YAML.**
|
|
4
|
+
|
|
5
|
+
MywayConfig extends [anyway_config](https://github.com/palkan/anyway_config) with:
|
|
6
|
+
|
|
7
|
+
- **XDG config file loading** - Respects `~/.config/<app>/config.yml`
|
|
8
|
+
- **Bundled defaults** - Ship defaults with your gem
|
|
9
|
+
- **Auto-configuration** - Define structure once in YAML, access everywhere
|
|
10
|
+
- **Hash-like behavior** - `Enumerable` support for config sections
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
# Define once in YAML
|
|
16
|
+
defaults:
|
|
17
|
+
database:
|
|
18
|
+
host: localhost
|
|
19
|
+
port: 5432
|
|
20
|
+
log_level: :info
|
|
21
|
+
|
|
22
|
+
production:
|
|
23
|
+
database:
|
|
24
|
+
host: prod-db.example.com
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# Access with clean Ruby syntax
|
|
29
|
+
config.database.host # => "localhost"
|
|
30
|
+
config.database[:host] # => "localhost"
|
|
31
|
+
config.database['host'] # => "localhost"
|
|
32
|
+
config.log_level # => :info
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Example
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
require "myway_config"
|
|
39
|
+
|
|
40
|
+
module MyApp
|
|
41
|
+
class Config < MywayConfig::Base
|
|
42
|
+
config_name :myapp
|
|
43
|
+
env_prefix :myapp
|
|
44
|
+
defaults_path File.expand_path("config/defaults.yml", __dir__)
|
|
45
|
+
auto_configure!
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.config
|
|
49
|
+
@config ||= Config.new
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Use it
|
|
54
|
+
MyApp.config.database.host
|
|
55
|
+
MyApp.config.production?
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
gem install myway_config
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or add to your Gemfile:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
gem "myway_config"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Configuration Priority
|
|
71
|
+
|
|
72
|
+
Values are loaded in priority order (lowest to highest):
|
|
73
|
+
|
|
74
|
+
1. Bundled defaults (`defaults.yml` - the `defaults:` section)
|
|
75
|
+
2. Environment overrides (`defaults.yml` - e.g., `production:` section)
|
|
76
|
+
3. XDG user config (`~/.config/<app>/config.yml`)
|
|
77
|
+
4. Project config (`./config/<app>.yml`)
|
|
78
|
+
5. Environment variables (`MYAPP_DATABASE__HOST=...`)
|
|
79
|
+
6. Constructor overrides
|
|
80
|
+
|
|
81
|
+
## Next Steps
|
|
82
|
+
|
|
83
|
+
- [Installation](getting-started/installation.md) - Get up and running
|
|
84
|
+
- [Quick Start](getting-started/quick-start.md) - Build your first config
|
|
85
|
+
- [Guides](guides/index.md) - Deep dive into features
|
|
86
|
+
- [API Reference](api/index.md) - Complete API documentation
|