blueprint_config 1.4.0 → 1.5.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 +4 -4
- data/.idea/blue_config.iml +33 -0
- data/README.md +403 -7
- data/lib/blueprint_config/backend/active_record.rb +8 -0
- data/lib/blueprint_config/backend/credentials.rb +29 -10
- data/lib/blueprint_config/backend/env.rb +12 -0
- data/lib/blueprint_config/backend/memory.rb +50 -0
- data/lib/blueprint_config/backend/yaml.rb +4 -0
- data/lib/blueprint_config/configuration.rb +33 -0
- data/lib/blueprint_config/options_hash.rb +20 -0
- data/lib/blueprint_config/version.rb +1 -1
- data/lib/blueprint_config.rb +13 -0
- data/spec/backend_source_tracking_spec.rb +130 -0
- data/spec/blueprint_config/backend/memory_spec.rb +181 -0
- data/spec/configuration_integration_spec.rb +246 -0
- data/spec/configuration_memory_methods_spec.rb +145 -0
- data/spec/memory_integration_spec.rb +191 -0
- data/spec/options_hash_with_sources_spec.rb +132 -0
- data/spec/spec_helper.rb +3 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1f80d9fbe572d7bbbef333547cb8423732f6ec75765be47e31bcefe3a0a3c0e
|
4
|
+
data.tar.gz: 179b6bed1dbc51c9521ec5c6d1ff0951f0d6f5bc1b1436216e0436c1b3b2e86b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcbe5013a6e6217df4c358a4abbb9ec26fc1f476ffdcff23acad47d06733a99096f3b1bcf6cb114eee06384428a16406a8b188e2c9b2c5992cccfa4e13a55a34
|
7
|
+
data.tar.gz: 30bf9fd227b7f6137adc0b66d92d81ed2cec697754995515b96cea57230072f9c00b72074cbfc1b10e72adc5514446824b25068addab9d1da51923228292fff7
|
data/.idea/blue_config.iml
CHANGED
@@ -69,4 +69,37 @@
|
|
69
69
|
</RakeTaskImpl>
|
70
70
|
</option>
|
71
71
|
</component>
|
72
|
+
<component name="RakeTasksCache-v2">
|
73
|
+
<option name="myRootTask">
|
74
|
+
<RakeTaskImpl id="rake">
|
75
|
+
<subtasks>
|
76
|
+
<RakeTaskImpl description="Build blueprint_config-1.4.0.gem into the pkg directory" fullCommand="build" id="build" />
|
77
|
+
<RakeTaskImpl id="build">
|
78
|
+
<subtasks>
|
79
|
+
<RakeTaskImpl description="Generate SHA512 checksum if blueprint_config-1.4.0.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
|
80
|
+
</subtasks>
|
81
|
+
</RakeTaskImpl>
|
82
|
+
<RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
|
83
|
+
<RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
|
84
|
+
<RakeTaskImpl description="Build and install blueprint_config-1.4.0.gem into system gems" fullCommand="install" id="install" />
|
85
|
+
<RakeTaskImpl id="install">
|
86
|
+
<subtasks>
|
87
|
+
<RakeTaskImpl description="Build and install blueprint_config-1.4.0.gem into system gems without network access" fullCommand="install:local" id="local" />
|
88
|
+
</subtasks>
|
89
|
+
</RakeTaskImpl>
|
90
|
+
<RakeTaskImpl description="Create tag v1.4.0 and build and push blueprint_config-1.4.0.gem to rubygems.org" fullCommand="release[remote]" id="release[remote]" />
|
91
|
+
<RakeTaskImpl description="Run RSpec code examples" fullCommand="spec" id="spec" />
|
92
|
+
<RakeTaskImpl description="" fullCommand="default" id="default" />
|
93
|
+
<RakeTaskImpl description="" fullCommand="release" id="release" />
|
94
|
+
<RakeTaskImpl id="release">
|
95
|
+
<subtasks>
|
96
|
+
<RakeTaskImpl description="" fullCommand="release:guard_clean" id="guard_clean" />
|
97
|
+
<RakeTaskImpl description="" fullCommand="release:rubygem_push" id="rubygem_push" />
|
98
|
+
<RakeTaskImpl description="" fullCommand="release:source_control_push" id="source_control_push" />
|
99
|
+
</subtasks>
|
100
|
+
</RakeTaskImpl>
|
101
|
+
</subtasks>
|
102
|
+
</RakeTaskImpl>
|
103
|
+
</option>
|
104
|
+
</component>
|
72
105
|
</module>
|
data/README.md
CHANGED
@@ -33,12 +33,135 @@ and load configuration in following order:
|
|
33
33
|
- settings in database
|
34
34
|
- config/app.local.yml
|
35
35
|
|
36
|
-
Settings
|
36
|
+
Settings from database will be available only after rails app initialization.
|
37
37
|
Everything else can be used in initializers.
|
38
38
|
|
39
|
-
##
|
39
|
+
## Rails Credentials Handling
|
40
40
|
|
41
|
-
|
41
|
+
BlueprintConfig integrates with Rails credentials system. When Rails is available, it uses `Rails.application.credentials` which automatically handles both global and environment-specific credentials.
|
42
|
+
|
43
|
+
### Environment-Specific Credentials (Rails 6+)
|
44
|
+
|
45
|
+
Rails 6+ supports environment-specific credentials that are automatically merged:
|
46
|
+
- Global: `config/credentials.yml.enc` (with `config/master.key`)
|
47
|
+
- Environment-specific: `config/credentials/[environment].yml.enc` (with `config/credentials/[environment].key`)
|
48
|
+
|
49
|
+
Example structure:
|
50
|
+
```
|
51
|
+
config/
|
52
|
+
├── credentials.yml.enc # Global credentials
|
53
|
+
├── master.key # Global key (not committed)
|
54
|
+
└── credentials/
|
55
|
+
├── development.yml.enc # Development-only credentials
|
56
|
+
├── development.key # Development key (not committed)
|
57
|
+
├── production.yml.enc # Production-only credentials
|
58
|
+
└── production.key # Production key (not committed)
|
59
|
+
```
|
60
|
+
|
61
|
+
When both exist, Rails merges them with environment-specific values taking precedence. BlueprintConfig automatically receives the merged result through `Rails.application.credentials`.
|
62
|
+
|
63
|
+
Example:
|
64
|
+
```ruby
|
65
|
+
# In config/credentials.yml.enc (global):
|
66
|
+
aws:
|
67
|
+
region: us-east-1
|
68
|
+
bucket: myapp
|
69
|
+
|
70
|
+
# In config/credentials/production.yml.enc:
|
71
|
+
aws:
|
72
|
+
access_key_id: PROD_KEY_123
|
73
|
+
secret_access_key: PROD_SECRET_456
|
74
|
+
|
75
|
+
# Result in production:
|
76
|
+
AppConfig.aws.region # => "us-east-1" (from global)
|
77
|
+
AppConfig.aws.bucket # => "myapp" (from global)
|
78
|
+
AppConfig.aws.access_key_id # => "PROD_KEY_123" (from production)
|
79
|
+
AppConfig.aws.secret_access_key # => "PROD_SECRET_456" (from production)
|
80
|
+
```
|
81
|
+
|
82
|
+
## Configuration Recommendations
|
83
|
+
|
84
|
+
### Where to Put Your Settings
|
85
|
+
|
86
|
+
Different types of configuration should be stored in different places based on their nature:
|
87
|
+
|
88
|
+
#### 1. **Credentials** → Use Rails Credentials
|
89
|
+
Sensitive data like API keys, passwords, and tokens:
|
90
|
+
```yaml
|
91
|
+
# config/credentials.yml.enc
|
92
|
+
stripe:
|
93
|
+
secret_key: sk_live_xxxxx
|
94
|
+
webhook_secret: whsec_xxxxx
|
95
|
+
database:
|
96
|
+
password: super_secret_password
|
97
|
+
external_api:
|
98
|
+
token: bearer_token_xxxxx
|
99
|
+
```
|
100
|
+
|
101
|
+
#### 2. **Application Configuration** → Use `config/app.yml`
|
102
|
+
Rarely changed settings that configure application behavior:
|
103
|
+
```yaml
|
104
|
+
# config/app.yml
|
105
|
+
smtp:
|
106
|
+
server: smtp.example.com
|
107
|
+
port: 587
|
108
|
+
domain: example.com
|
109
|
+
app:
|
110
|
+
host: app.example.com
|
111
|
+
name: My Application
|
112
|
+
support_email: support@example.com
|
113
|
+
features:
|
114
|
+
signup_enabled: true
|
115
|
+
maintenance_mode: false
|
116
|
+
```
|
117
|
+
|
118
|
+
#### 3. **Dynamic Settings** → Use Database
|
119
|
+
Settings that need to change without redeployment:
|
120
|
+
```ruby
|
121
|
+
# Stored in settings table, managed via admin interface
|
122
|
+
BlueprintConfig::Setting.create(key: 'feature_flags.new_ui', value: 'true')
|
123
|
+
BlueprintConfig::Setting.create(key: 'rate_limits.api_calls', value: '1000')
|
124
|
+
BlueprintConfig::Setting.create(key: 'maintenance.message', value: 'Back at 3pm')
|
125
|
+
```
|
126
|
+
|
127
|
+
> **Automatic Reloading**: Database settings are automatically reloaded when changed. The system checks for updates every second and refreshes configuration if database records are newer than the cached values. This means changes take effect immediately without restarting the application.
|
128
|
+
|
129
|
+
#### 4. **Local Overrides** → Use `config/app.local.yml`
|
130
|
+
Development-specific overrides (never commit this file):
|
131
|
+
```yaml
|
132
|
+
# config/app.local.yml
|
133
|
+
smtp:
|
134
|
+
server: localhost
|
135
|
+
port: 1025 # Local MailCatcher
|
136
|
+
app:
|
137
|
+
host: localhost:3000
|
138
|
+
debug:
|
139
|
+
verbose_logging: true
|
140
|
+
```
|
141
|
+
|
142
|
+
### Configuration Precedence
|
143
|
+
|
144
|
+
Settings are loaded in this order (later sources override earlier ones):
|
145
|
+
1. `config/app.yml` - Base configuration
|
146
|
+
2. Rails credentials - Secure/sensitive data
|
147
|
+
3. ENV variables - Deployment-specific overrides
|
148
|
+
4. Database settings - Runtime-configurable values
|
149
|
+
5. `config/app.local.yml` - Local development overrides
|
150
|
+
|
151
|
+
## Accessing Configuration Variables
|
152
|
+
|
153
|
+
The recommended access style is using dot notation (member access syntax):
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
# Recommended approach
|
157
|
+
AppConfig.action_mailer.smtp_settings.address
|
158
|
+
AppConfig.stripe.secret_key
|
159
|
+
AppConfig.features.signup_enabled
|
160
|
+
|
161
|
+
# This provides clean, readable code that mirrors your configuration structure
|
162
|
+
```
|
163
|
+
|
164
|
+
You can access configuration variables in any of following ways:
|
42
165
|
|
43
166
|
### Member access syntax:
|
44
167
|
|
@@ -58,7 +181,7 @@ AppConfig.some.var
|
|
58
181
|
^^^^
|
59
182
|
```
|
60
183
|
|
61
|
-
If nil is suitable as a default value
|
184
|
+
If nil is suitable as a default value you can use safe navigation operator
|
62
185
|
|
63
186
|
```ruby
|
64
187
|
irb(main):001> AppConfig.some&.var
|
@@ -68,7 +191,7 @@ irb(main):001> AppConfig.some&.var
|
|
68
191
|
|
69
192
|
|
70
193
|
|
71
|
-
|
194
|
+
Optionally you can use bang methods to always raise exception when variable is not defined
|
72
195
|
|
73
196
|
```ruby
|
74
197
|
irb(main):001> AppConfig.some
|
@@ -88,7 +211,7 @@ irb(main):002> AppConfig.host?
|
|
88
211
|
=> true
|
89
212
|
```
|
90
213
|
|
91
|
-
Note: Because question mark methods return Boolean you cannot chain
|
214
|
+
Note: Because question mark methods return Boolean you cannot chain them.
|
92
215
|
|
93
216
|
### Hash access syntax
|
94
217
|
You can use both symbols or strings as keys
|
@@ -128,7 +251,7 @@ irb(main):004> AppConfig.fetch(:var){123}
|
|
128
251
|
|
129
252
|
### Dig
|
130
253
|
|
131
|
-
Dig permits to
|
254
|
+
Dig permits to safely get value in nested structures (including arrays)
|
132
255
|
|
133
256
|
```ruby
|
134
257
|
irb(main):001> AppConfig.dig(:action_mailer, :smtp_settings, :address)
|
@@ -146,6 +269,279 @@ irb(main):002> AppConfig.dig!(:action, :smtp_settings, :address)
|
|
146
269
|
|
147
270
|
Whenever possible exception message specifies which key is missing.
|
148
271
|
|
272
|
+
## Testing with Memory Backend
|
273
|
+
|
274
|
+
In test environments, BlueprintConfig automatically includes a memory backend that allows you to temporarily set configuration values for testing purposes. The memory backend has the highest priority and will override all other configuration sources.
|
275
|
+
|
276
|
+
### Setting Values in Tests
|
277
|
+
|
278
|
+
You can use `AppConfig.set` to temporarily override configuration values:
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
# Set a single value
|
282
|
+
AppConfig.set('smtp.server', 'test.localhost')
|
283
|
+
|
284
|
+
# Set multiple values with a hash
|
285
|
+
AppConfig.set(
|
286
|
+
smtp: {
|
287
|
+
server: 'localhost',
|
288
|
+
port: 1025
|
289
|
+
},
|
290
|
+
features: {
|
291
|
+
new_ui: true
|
292
|
+
}
|
293
|
+
)
|
294
|
+
|
295
|
+
# Set deeply nested values
|
296
|
+
AppConfig.set('app.mail.settings', {
|
297
|
+
from: 'test@example.com',
|
298
|
+
reply_to: 'noreply@example.com'
|
299
|
+
})
|
300
|
+
```
|
301
|
+
|
302
|
+
### RSpec Usage Pattern
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
RSpec.describe 'MyFeature' do
|
306
|
+
context 'with feature enabled' do
|
307
|
+
before do
|
308
|
+
AppConfig.set('features.new_ui', true)
|
309
|
+
end
|
310
|
+
|
311
|
+
after do
|
312
|
+
AppConfig.clear_memory!
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'behaves differently' do
|
316
|
+
expect(AppConfig.features.new_ui).to be true
|
317
|
+
# test your feature
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
323
|
+
### Shared Examples and Contexts
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
RSpec.shared_context 'with premium features' do
|
327
|
+
before do
|
328
|
+
AppConfig.set(
|
329
|
+
features: {
|
330
|
+
premium: true,
|
331
|
+
advanced_analytics: true,
|
332
|
+
api_limit: 10000
|
333
|
+
}
|
334
|
+
)
|
335
|
+
end
|
336
|
+
|
337
|
+
after do
|
338
|
+
AppConfig.clear_memory!
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Use in your specs
|
343
|
+
RSpec.describe PremiumService do
|
344
|
+
include_context 'with premium features'
|
345
|
+
|
346
|
+
it 'allows advanced operations' do
|
347
|
+
expect(AppConfig.features.premium).to be true
|
348
|
+
# test premium functionality
|
349
|
+
end
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
### Clearing Memory
|
354
|
+
|
355
|
+
Use `AppConfig.clear_memory!` to clear all memory-stored values:
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
# In RSpec configuration
|
359
|
+
RSpec.configure do |config|
|
360
|
+
config.after(:each) do
|
361
|
+
AppConfig.clear_memory! if defined?(AppConfig)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# Or manually in specific tests
|
366
|
+
AppConfig.clear_memory!
|
367
|
+
```
|
368
|
+
|
369
|
+
### Important Notes
|
370
|
+
|
371
|
+
1. The memory backend is only available in test environment (`Rails.env.test?`)
|
372
|
+
2. Memory values have the highest priority and override all other sources
|
373
|
+
3. Values are stored in memory only and are not persisted
|
374
|
+
4. Remember to clear memory between tests to avoid test pollution
|
375
|
+
5. The `set` method will raise an error if used outside test environment
|
376
|
+
|
377
|
+
## Debugging and Inspection
|
378
|
+
|
379
|
+
### Viewing Configuration Sources
|
380
|
+
|
381
|
+
BlueprintConfig tracks where each configuration value comes from. You can use the `source` method to see which backend provided a specific value:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
# Get the source of a specific configuration value
|
385
|
+
AppConfig.config.source(:smtp, :server)
|
386
|
+
# => "BlueprintConfig::Backend::YAML(config/app.yml) smtp.server"
|
387
|
+
|
388
|
+
AppConfig.config.source(:database, :password)
|
389
|
+
# => "BlueprintConfig::Backend::Credentials(global + production) database.password"
|
390
|
+
|
391
|
+
# In test environment with memory backend
|
392
|
+
AppConfig.set('feature.enabled', true)
|
393
|
+
AppConfig.config.source(:feature, :enabled)
|
394
|
+
# => "BlueprintConfig::Backend::Memory feature.enabled"
|
395
|
+
|
396
|
+
# For environment variables with specific configuration
|
397
|
+
AppConfig.config.source(:api, :key)
|
398
|
+
# => "BlueprintConfig::Backend::ENV(whitelist_prefixes: API_, APP_) api.key"
|
399
|
+
|
400
|
+
# For local overrides
|
401
|
+
AppConfig.config.source(:debug, :verbose)
|
402
|
+
# => "BlueprintConfig::Backend::YAML(config/app.local.yml) debug.verbose"
|
403
|
+
|
404
|
+
# For database settings
|
405
|
+
AppConfig.config.source(:rate_limit, :max_requests)
|
406
|
+
# => "BlueprintConfig::Backend::ActiveRecord(settings table) rate_limit.max_requests"
|
407
|
+
```
|
408
|
+
|
409
|
+
### Inspecting All Configuration
|
410
|
+
|
411
|
+
To view all configuration values as a hash:
|
412
|
+
|
413
|
+
```ruby
|
414
|
+
# Get all configuration as a Ruby hash
|
415
|
+
AppConfig.to_h
|
416
|
+
# or
|
417
|
+
AppConfig.config.to_h
|
418
|
+
# => {
|
419
|
+
# smtp: {
|
420
|
+
# server: "smtp.gmail.com",
|
421
|
+
# port: 587,
|
422
|
+
# domain: "example.com"
|
423
|
+
# },
|
424
|
+
# database: {
|
425
|
+
# pool: 5,
|
426
|
+
# timeout: 5000
|
427
|
+
# },
|
428
|
+
# features: {
|
429
|
+
# signup_enabled: true,
|
430
|
+
# beta_features: false
|
431
|
+
# }
|
432
|
+
# }
|
433
|
+
|
434
|
+
# Pretty print configuration in console
|
435
|
+
require 'pp'
|
436
|
+
pp AppConfig.to_h
|
437
|
+
|
438
|
+
# Convert to YAML for easier reading
|
439
|
+
require 'yaml'
|
440
|
+
puts AppConfig.to_h.to_yaml
|
441
|
+
# ---
|
442
|
+
# :smtp:
|
443
|
+
# :server: smtp.gmail.com
|
444
|
+
# :port: 587
|
445
|
+
# :domain: example.com
|
446
|
+
# :database:
|
447
|
+
# :pool: 5
|
448
|
+
# :timeout: 5000
|
449
|
+
# :features:
|
450
|
+
# :signup_enabled: true
|
451
|
+
# :beta_features: false
|
452
|
+
```
|
453
|
+
|
454
|
+
### Viewing Configuration with Sources
|
455
|
+
|
456
|
+
To see both values and their sources:
|
457
|
+
|
458
|
+
```ruby
|
459
|
+
# Get configuration with source information
|
460
|
+
AppConfig.with_sources
|
461
|
+
# or
|
462
|
+
AppConfig.config.with_sources
|
463
|
+
# => {
|
464
|
+
# smtp: {
|
465
|
+
# server: {
|
466
|
+
# value: "smtp.gmail.com",
|
467
|
+
# source: "BlueprintConfig::Backend::YAML(config/app.yml)"
|
468
|
+
# },
|
469
|
+
# port: {
|
470
|
+
# value: 587,
|
471
|
+
# source: "BlueprintConfig::Backend::YAML(config/app.yml)"
|
472
|
+
# },
|
473
|
+
# username: {
|
474
|
+
# value: "user@example.com",
|
475
|
+
# source: "BlueprintConfig::Backend::Credentials(production)"
|
476
|
+
# }
|
477
|
+
# },
|
478
|
+
# features: {
|
479
|
+
# beta_enabled: {
|
480
|
+
# value: true,
|
481
|
+
# source: "BlueprintConfig::Backend::Memory"
|
482
|
+
# }
|
483
|
+
# },
|
484
|
+
# debug: {
|
485
|
+
# verbose: {
|
486
|
+
# value: true,
|
487
|
+
# source: "BlueprintConfig::Backend::YAML(config/app.local.yml)"
|
488
|
+
# }
|
489
|
+
# }
|
490
|
+
# }
|
491
|
+
|
492
|
+
# Pretty print with sources
|
493
|
+
require 'pp'
|
494
|
+
pp AppConfig.config.with_sources
|
495
|
+
|
496
|
+
# Convert to YAML for easier reading
|
497
|
+
require 'yaml'
|
498
|
+
puts AppConfig.config.with_sources.to_yaml
|
499
|
+
```
|
500
|
+
|
501
|
+
### Checking Available Backends
|
502
|
+
|
503
|
+
To see which backends are currently loaded:
|
504
|
+
|
505
|
+
```ruby
|
506
|
+
# List all backends with their configuration
|
507
|
+
AppConfig.backends.each do |backend|
|
508
|
+
puts backend.source
|
509
|
+
end
|
510
|
+
# Output:
|
511
|
+
# BlueprintConfig::Backend::YAML(config/app.yml)
|
512
|
+
# BlueprintConfig::Backend::Credentials(global + production)
|
513
|
+
# BlueprintConfig::Backend::ENV(whitelist_prefixes: API_, APP_)
|
514
|
+
# BlueprintConfig::Backend::ActiveRecord(settings table)
|
515
|
+
# BlueprintConfig::Backend::YAML(config/app.local.yml)
|
516
|
+
# BlueprintConfig::Backend::Memory
|
517
|
+
|
518
|
+
# Check specific backend
|
519
|
+
AppConfig.backends[:memory] # => BlueprintConfig::Backend::Memory instance or nil
|
520
|
+
AppConfig.backends[:app] # => BlueprintConfig::Backend::YAML instance
|
521
|
+
```
|
522
|
+
|
523
|
+
### Configuration Load Order
|
524
|
+
|
525
|
+
The default configuration load order (later sources override earlier ones):
|
526
|
+
|
527
|
+
1. `app` - YAML file (config/app.yml)
|
528
|
+
2. `credentials` - Rails credentials
|
529
|
+
3. `env` - Environment variables
|
530
|
+
4. `db` - Database settings (after Rails initialization)
|
531
|
+
5. `app_local` - Local YAML overrides (config/app.local.yml)
|
532
|
+
6. `memory` - Test-only memory backend (highest priority in test environment)
|
533
|
+
|
534
|
+
### Source Tracking Details
|
535
|
+
|
536
|
+
BlueprintConfig provides detailed source information for debugging:
|
537
|
+
|
538
|
+
- **YAML files**: Shows the file path (e.g., `config/app.yml`)
|
539
|
+
- **Credentials**: Shows whether using global only or merged with environment-specific (e.g., `global + production`)
|
540
|
+
- **Environment variables**: Shows any whitelist configuration
|
541
|
+
- **Database**: Indicates if connected to settings table
|
542
|
+
- **Memory**: Simple indicator for test values
|
543
|
+
|
544
|
+
Note: While YAML line numbers are not currently tracked, the file path information helps identify which YAML file contains specific values. For credentials, Rails automatically merges global and environment-specific files, and the source indicates which files are in use.
|
149
545
|
|
150
546
|
## License
|
151
547
|
|
@@ -114,6 +114,14 @@ module BlueprintConfig
|
|
114
114
|
|
115
115
|
@updated_at.present? && @updated_at >= max_updated_at
|
116
116
|
end
|
117
|
+
|
118
|
+
def source
|
119
|
+
if @configured && table_exist?
|
120
|
+
"#{self.class.name}(settings table)"
|
121
|
+
else
|
122
|
+
"#{self.class.name}(not available)"
|
123
|
+
end
|
124
|
+
end
|
117
125
|
end
|
118
126
|
end
|
119
127
|
end
|
@@ -6,21 +6,40 @@ module BlueprintConfig
|
|
6
6
|
module Backend
|
7
7
|
class Credentials < Base
|
8
8
|
def load_keys
|
9
|
-
|
9
|
+
if defined?(Rails)
|
10
|
+
# When Rails is available, use Rails.application.credentials
|
11
|
+
# Rails already handles merging global and environment-specific credentials
|
12
|
+
Rails.application.credentials.to_h
|
13
|
+
else
|
14
|
+
# Standalone mode - just load global credentials
|
15
|
+
standalone_credentials.to_h
|
16
|
+
end
|
10
17
|
end
|
11
18
|
|
12
|
-
def
|
13
|
-
if defined?(Rails)
|
14
|
-
|
19
|
+
def source
|
20
|
+
if defined?(Rails) && Rails.env
|
21
|
+
# Check if environment-specific credentials exist
|
22
|
+
env_path = "config/credentials/#{Rails.env}.yml.enc"
|
23
|
+
if File.exist?(env_path)
|
24
|
+
"#{self.class.name}(global + #{Rails.env})"
|
25
|
+
else
|
26
|
+
"#{self.class.name}(global)"
|
27
|
+
end
|
15
28
|
else
|
16
|
-
|
17
|
-
config_path: 'config/credentials.yml.enc',
|
18
|
-
key_path: 'config/master.key',
|
19
|
-
env_key: 'RAILS_MASTER_KEY',
|
20
|
-
raise_if_missing_key: false
|
21
|
-
)
|
29
|
+
"#{self.class.name}"
|
22
30
|
end
|
23
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def standalone_credentials
|
36
|
+
ActiveSupport::EncryptedConfiguration.new(
|
37
|
+
config_path: 'config/credentials.yml.enc',
|
38
|
+
key_path: 'config/master.key',
|
39
|
+
env_key: 'RAILS_MASTER_KEY',
|
40
|
+
raise_if_missing_key: false
|
41
|
+
)
|
42
|
+
end
|
24
43
|
end
|
25
44
|
end
|
26
45
|
end
|
@@ -42,6 +42,18 @@ module BlueprintConfig
|
|
42
42
|
|
43
43
|
nest_hash(filtered_env, @options[:nest_separator] || '_')
|
44
44
|
end
|
45
|
+
|
46
|
+
def source
|
47
|
+
details = []
|
48
|
+
details << "whitelist_keys: #{@options[:whitelist_keys].join(', ')}" if @options[:whitelist_keys]
|
49
|
+
details << "whitelist_prefixes: #{@options[:whitelist_prefixes].join(', ')}" if @options[:whitelist_prefixes]
|
50
|
+
|
51
|
+
if details.any?
|
52
|
+
"#{self.class.name}(#{details.join(', ')})"
|
53
|
+
else
|
54
|
+
self.class.name
|
55
|
+
end
|
56
|
+
end
|
45
57
|
end
|
46
58
|
end
|
47
59
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BlueprintConfig
|
4
|
+
module Backend
|
5
|
+
class Memory < Base
|
6
|
+
def initialize
|
7
|
+
super
|
8
|
+
@store = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_keys
|
12
|
+
# Return the flattened store which will be nested by BlueprintConfig
|
13
|
+
nest_hash(@store, '.')
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(key, value)
|
17
|
+
if value.is_a?(Hash)
|
18
|
+
# Handle nested hashes - store them properly for nested access
|
19
|
+
nested_hash = flatten_hash(value, key.to_s)
|
20
|
+
@store.merge!(nested_hash)
|
21
|
+
else
|
22
|
+
@store[key.to_s] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear
|
27
|
+
@store.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
def fresh?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def flatten_hash(hash, parent_key = '')
|
37
|
+
result = {}
|
38
|
+
hash.each do |k, v|
|
39
|
+
new_key = parent_key.empty? ? k.to_s : "#{parent_key}.#{k}"
|
40
|
+
if v.is_a?(Hash)
|
41
|
+
result.merge!(flatten_hash(v, new_key))
|
42
|
+
else
|
43
|
+
result[new_key] = v
|
44
|
+
end
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -60,5 +60,38 @@ module BlueprintConfig
|
|
60
60
|
object
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
def set(key, value = nil)
|
65
|
+
memory_backend = @backends[:memory]
|
66
|
+
raise "Memory backend not configured. Only available in test environment." unless memory_backend
|
67
|
+
|
68
|
+
if key.is_a?(Hash)
|
69
|
+
# Handle hash argument: AppConfig.set(foo: { bar: 'baz' })
|
70
|
+
key.each do |k, v|
|
71
|
+
set(k.to_s, v)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
memory_backend.set(key.to_s, value)
|
75
|
+
end
|
76
|
+
reload!
|
77
|
+
end
|
78
|
+
|
79
|
+
def clear_memory!
|
80
|
+
memory_backend = @backends[:memory]
|
81
|
+
return unless memory_backend
|
82
|
+
|
83
|
+
memory_backend.clear
|
84
|
+
reload!
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_h
|
88
|
+
reload! unless backends&.fresh?
|
89
|
+
config.to_h
|
90
|
+
end
|
91
|
+
|
92
|
+
def with_sources
|
93
|
+
reload! unless backends&.fresh?
|
94
|
+
config.with_sources
|
95
|
+
end
|
63
96
|
end
|
64
97
|
end
|