const_conf 0.0.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.
data/README.md ADDED
@@ -0,0 +1,789 @@
1
+ # ConstConf
2
+
3
+ **ConstConf**: A robust, thread-safe configuration management library for Ruby
4
+ applications.
5
+
6
+ ## Description
7
+
8
+ ConstConf provides a clean DSL for defining configuration settings with
9
+ environment variable support, default values, required validation, decoding
10
+ logic, and descriptive metadata. It offers file-based configuration plugins,
11
+ XDG Base Directory Specification compliance, and seamless Rails integration.
12
+
13
+ ## Installation
14
+
15
+ To install ConstConf, you can use the following methods:
16
+
17
+ 1. Type
18
+
19
+ ```shell
20
+ gem install const_conf
21
+ ```
22
+
23
+ in your terminal.
24
+
25
+ 1. Or add the line
26
+
27
+ ```ruby
28
+ gem 'const_conf'
29
+ ```
30
+
31
+ to your Gemfile and run `bundle install` in your terminal.
32
+
33
+ ## Usage
34
+
35
+ Define configuration in a module:
36
+
37
+ ```ruby
38
+ require 'const_conf'
39
+
40
+ module AppConfig
41
+ include ConstConf
42
+
43
+ plugin ConstConf::FilePlugin # Use FilePlugin
44
+ plugin ConstConf::DirPlugin # Use DirPlugin
45
+
46
+ description 'Application Configuration'
47
+ prefix '' # Use empty prefix for flat environment variable names
48
+
49
+ # Simple environment variable setting
50
+ DATABASE_URL = set do
51
+ description 'Database connection string'
52
+ required true
53
+ sensitive true
54
+ end
55
+
56
+ # File-based configuration
57
+ API_KEY = set do
58
+ prefix 'CONFIG'
59
+ description 'API key from file'
60
+ default file('config/api.key', required: true)
61
+ sensitive true
62
+ decode(&:chomp)
63
+ end
64
+
65
+ # Directory-based configuration with XDG support
66
+ CONFIG_FROM_DIR = set do
67
+ description 'Configuration directory path'
68
+ default dir('myapp', 'config.yaml', env_var_name: 'XDG_CONFIG_HOME')
69
+ decode { require 'yaml'; YAML.load(it) }
70
+ end
71
+ end
72
+
73
+ # Access settings
74
+ puts AppConfig::DATABASE_URL # From ENV['DATABASE_URL']
75
+ puts AppConfig::API_KEY # Default file config/api.key, or ENV['CONFIG_API_KEY']
76
+ puts AppConfig::CONFIG_FROM_DIR # From directory structure at ENV['XDG_CONFIG_HOME'] + "/myapp"
77
+ # =>
78
+ # postgresql://user:pass@localhost/myapp
79
+ # sk-1234567890abcdef1234567890abcdef
80
+ # {"host" => "localhost", "port" => 3000}
81
+ ```
82
+
83
+ In Rails it is best to require your configuration in `config/app_config.rb` as soon
84
+ as the bundled gems were loaded, so subsequent initiallizers can access the
85
+ created setting constants:
86
+ init
87
+ ```ruby
88
+ Bundler.require(*Rails.groups)
89
+ require_relative 'app_config.rb'
90
+
91
+ module MyApp
92
+
93
+ end
94
+ ```
95
+
96
+ ### Note that **Predicate Methods (`?`)** are defined
97
+
98
+ If a setting is `active?`, the predicate method returns a truthy value, which
99
+ then can be used like this:
100
+
101
+ ```ruby
102
+ # Check if active?
103
+ if database_url = AppConfig::DATABASE_URL?
104
+ connect_to_database with: database_url
105
+ else
106
+ STDERR.puts "No DATABASE_URL configured for app!"
107
+ end
108
+ ```
109
+
110
+ Or `nil` is returned, which can then be handled accordingly.
111
+
112
+ ### Configuration View Explanation
113
+
114
+ The `AppConfig.view` output shows the complete configuration hierarchy with
115
+ detailed metadata for each setting. Sensitive values are displayed as 🤫 to
116
+ protect confidential information like passwords and API keys.
117
+
118
+ Key indicators in the view:
119
+
120
+
121
+ | Yes | No | Purpose |
122
+ |:---:|:---:|----------------------|
123
+ | 🔒 | ⚪ | Sensitive data (e.g., passwords, tokens)|
124
+ | 🤫 | … | Suppressed output of value if sensitive|
125
+ | 🔴 | ⚪ | Required settings that must be configured|
126
+ | 🔧 | ⚪ | Configured values from ENV |
127
+ | 🙈 | ⚪ | ENV var was ignored |
128
+ | 🟢 | ⚪ | Setting is active (? method returns truty value) |
129
+ | ⚙️ | ⚪ | Decoding logic applied to transform raw input |
130
+ | ✅ ┊ ☑️ | ❌ | Validation checks have passed or failed |
131
+
132
+ The view helps developers understand how configuration is resolved from
133
+ multiple sources and validate that settings are properly configured while
134
+ protecting sensitive data.
135
+
136
+ ```
137
+ AppConfig # Application Configuration
138
+ ├─ prefix ""
139
+ ├─ 3 settings
140
+ ├─ AppConfig::DATABASE_URL # Database connection string
141
+ │ ├─ prefix ""
142
+ │ ├─ env var name DATABASE_URL
143
+ │ ├─ env var (orig.) 🤫
144
+ │ ├─ default 🤫
145
+ │ ├─ value 🤫
146
+ │ ├─ sensitive 🔒
147
+ │ ├─ required 🔴
148
+ │ ├─ configured 🔧
149
+ │ ├─ ignored ⚪
150
+ │ ├─ active 🟢
151
+ │ ├─ decoding ⚪
152
+ │ └─ checked ☑️
153
+ ├─ AppConfig::API_KEY # API key from file
154
+ │ ├─ prefix "CONFIG"
155
+ │ ├─ env var name CONFIG_API_KEY
156
+ │ ├─ env var (orig.) 🤫
157
+ │ ├─ default 🤫
158
+ │ ├─ value 🤫
159
+ │ ├─ sensitive 🔒
160
+ │ ├─ required ⚪
161
+ │ ├─ configured ⚪
162
+ │ ├─ ignored ⚪
163
+ │ ├─ active 🟢
164
+ │ ├─ decoding ⚙️
165
+ │ └─ checked ☑️
166
+ └─ AppConfig::CONFIG_FROM_DIR # Configuration directory path
167
+ ├─ prefix ""
168
+ ├─ env var name CONFIG_FROM_DIR
169
+ ├─ env var (orig.) nil
170
+ ├─ default "host: 'localhost'\nport: 3000\n"
171
+ ├─ value {"host" => "localhost", "port" => 3000}
172
+ ├─ sensitive ⚪
173
+ ├─ required ⚪
174
+ ├─ configured ⚪
175
+ ├─ ignored ⚪
176
+ ├─ active 🟢
177
+ ├─ decoding ⚙️
178
+ └─ checked ☑️
179
+
180
+ ```
181
+
182
+ ### Use of Plugins
183
+
184
+ ConstConf provides extensible plugin architecture for adding configuration
185
+ sources and behaviors, as can be seen in the example above. The library
186
+ includes two main built-in plugins:
187
+
188
+ ```mermaid
189
+ graph TD
190
+ A[ConstConf Module] --> B[set do Block]
191
+ A --> C[Plugin System]
192
+
193
+ C --> D[FilePlugin]
194
+ C --> E[DirPlugin]
195
+ C --> F[JSONPlugin]
196
+ C --> G[YAMLPlugin]
197
+ C --> H[Custom Plugins]
198
+
199
+ B --> I[DSL Methods]
200
+ I --> J[file method]
201
+ I --> K[dir method]
202
+ I --> L[json method]
203
+ I --> M[yaml method]
204
+ I --> N[Core DSL methods]
205
+
206
+ D --> O[File-based Configuration]
207
+ E --> P[Directory-based Configuration]
208
+ F --> Q[JSON Configuration]
209
+ G --> R[YAML Configuration]
210
+ H --> S[Custom Configuration Sources]
211
+
212
+ O --> T[File Reading Logic]
213
+ P --> U[Directory Traversal Logic]
214
+ Q --> V[JSON Parsing Logic]
215
+ R --> W[YAML Parsing Logic]
216
+ S --> X[Custom Logic]
217
+
218
+ I --> Y[Configuration Resolution Process]
219
+ Y --> Z[ENV Variable Lookup]
220
+ Y --> AA[Default Value Handling]
221
+ Y --> AB[Validation & Processing]
222
+
223
+ style A fill:#e1f5fe
224
+ style B fill:#f3e5f5
225
+ style C fill:#e8f5e9
226
+ style D fill:#fff3e0
227
+ style E fill:#fce4ec
228
+ style F fill:#f1f8e9
229
+ style G fill:#e0f2f1
230
+ style H fill:#f1f8e9
231
+ style I fill:#fafafa
232
+ style Y fill:#ffebee
233
+ ```
234
+
235
+ #### FilePlugin
236
+
237
+ Enables file-based configuration through the `file()` method:
238
+
239
+ ```ruby
240
+ API_KEY = set do
241
+ default file('config/api.key', required: true)
242
+
243
+ end
244
+ ```
245
+
246
+ #### DirPlugin
247
+
248
+ Enables directory-based configuration with XDG compliance through the `dir()`
249
+ method:
250
+
251
+ ```ruby
252
+ CONFIG_FROM_DIR = set do
253
+ default dir('myapp', 'config.yaml', env_var_name: 'XDG_CONFIG_HOME')
254
+
255
+ end
256
+ ```
257
+
258
+ #### JSONPlugin
259
+
260
+ Enables JSON-based configuration through the `json()` method:
261
+
262
+ ```ruby
263
+ CONFIG = set do
264
+ default json('config.json')
265
+ # or with custom object class:
266
+ # default json('config.json', object_class: MyCustomClass)
267
+
268
+ end
269
+ ```
270
+
271
+ #### YAMLPlugin
272
+
273
+ Enables YAML-based configuration through the `yaml()` method:
274
+
275
+ ```ruby
276
+ CONFIG = set do
277
+ default yaml('config.yaml')
278
+ # or with environment-specific loading:
279
+ # default yaml('config.yaml', env: true)
280
+ # or with explicit environment:
281
+ # default yaml('config.yaml', env: 'production')
282
+
283
+ end
284
+ ```
285
+
286
+ Plugins are registered using the `plugin` method at the module level. Multiple
287
+ plugins can be combined to provide flexible configuration sources.
288
+
289
+ When a plugin is loaded, it extends the DSL with additional methods and
290
+ behaviors that can be used within `set do` blocks. The plugin system allows for
291
+ easy extension of ConstConf's capabilities without modifying core
292
+ functionality.
293
+
294
+ ### Configuration Concepts Explained
295
+
296
+ #### **Description**
297
+
298
+ - **Purpose**: Human-readable explanation of what the setting is for (📝 is
299
+ always required to be provided)
300
+ - **Implementation**: The `description` accessor stores a string explaining the
301
+ setting's purpose
302
+ - **Usage**: `description 'Database connection string'`
303
+ - **Indicator**: Shows in view output as descriptive text next to each setting
304
+
305
+ #### **Prefix**
306
+
307
+ - **Purpose**: Namespace prefix used to construct environment variable names
308
+ - **Implementation**: The `prefix` accessor determines how environment
309
+ variables are named
310
+ - **Usage**: `prefix 'APP'` makes `API_KEY` become `APP_API_KEY`
311
+ - **Indicator**: Shows as prefix value in view output
312
+
313
+ #### **Decoding** (`decoding?`)
314
+
315
+ - **Purpose**: Transforms raw input values using a Proc
316
+ - **Implementation**: The `decode` accessor stores a Proc that processes the
317
+ value
318
+ - **Usage**: `decode(&:chomp)` removes whitespace, `decode { YAML.load(it) }`
319
+ parses YAML
320
+ - **Indicator**: Shows ⚙️ in view output when active
321
+
322
+ #### **Required** (`required?`)
323
+
324
+ - **Purpose**: Determines if a setting must have a valid value
325
+ - **Implementation**: Can be boolean (`true/false`) or Proc for conditional
326
+ validation
327
+ - **Usage**: `required true` (always required) or `required { !Rails.env.test?
328
+ }` (conditional)
329
+ - **Indicator**: Shows 🔴 in view output when required and not satisfied
330
+
331
+ #### **Configured** (`configured?`)
332
+
333
+ - **Purpose**: Indicates whether a value was actually provided (not just
334
+ default)
335
+ - **Implementation**: Checks if environment variable is set or default is used
336
+ - **Usage**: `configured?` returns true when `ENV['VAR_NAME']` exists or
337
+ default is non-nil
338
+ - **Indicator**: Shows 🔧 in view output when value is explicitly provided
339
+
340
+ #### **Checked** (`checked?`)
341
+
342
+ - **Purpose**: Validates that custom validation logic passes
343
+ - **Implementation**: The `check` accessor stores validation logic as Proc
344
+ - **Usage**: `check ->(setting) { setting.value >= 1024 }` for port validation
345
+ - **Indicator Logic**:
346
+ - **☑️** = No custom check defined (passes by default - `:unchecked_true`)
347
+ - **✅** = Custom check explicitly passes (returns `true`)
348
+ - **❌** = Custom check explicitly fails (returns `false`)
349
+
350
+ #### **Active** (`active?`)
351
+
352
+ - **Purpose**: Determines if the setting should be considered usable/active and
353
+ determines if the result of the AppConfig::FOOBAR? method is the
354
+ AppConfig::FOOBAR value if true.
355
+ - **Implementation**: Evaluates the `activated` property (defaults to
356
+ `:present?`)
357
+ - **Usage**: Can be `true`, `false`, `Symbol`, or `Proc` for custom activation
358
+ logic
359
+ - **Indicator**: Shows 🟢 in view output when active
360
+
361
+ #### **Ignored** (`ignored?`)
362
+
363
+ - **Purpose**: Skips processing this setting during configuration resolution
364
+ - **Implementation**: The `ignored` accessor prevents reading from ENV
365
+ variables
366
+ - **Usage**: Set to `true` to skip environment variable lookup for this setting
367
+ - **Indicator**: Shows 🙈 in view output when ignored
368
+
369
+ These concepts work together to provide a comprehensive configuration
370
+ management system that tracks the complete lifecycle and status of each setting
371
+ from definition through validation and usage.
372
+
373
+ ### Advanced Usage Examples
374
+
375
+ #### Nested Configuration Modules
376
+
377
+ ConstConf supports elegant nested module organization where prefixes are
378
+ automatically inherited and combined. When you define nested modules, the
379
+ library automatically constructs full environment variable names by combining
380
+ parent module prefixes with child module names. This creates a clean,
381
+ hierarchical configuration structure that's both maintainable and predictable.
382
+
383
+ ```ruby
384
+ require 'const_conf'
385
+
386
+ module AppConfig # default prefix 'APP_CONFIG'
387
+ include ConstConf
388
+
389
+ description 'Application Configuration'
390
+
391
+ module Database # default prefix 'APP_CONFIG_DATABASE'
392
+
393
+ description 'Database settings'
394
+
395
+ URL = set do # from ENV['APP_CONFIG_DATABASE_URL'] via default prefix
396
+ description 'Database connection URL'
397
+ end
398
+ end
399
+ end
400
+ ```
401
+
402
+ To access the nested configuration values in Ruby, you would reference them
403
+ through the full module path:
404
+
405
+ ```ruby
406
+ # Access the database URL setting
407
+ db_url = AppConfig::Database::URL
408
+
409
+ # Use it in your application
410
+ connect_to_database(db_url)
411
+
412
+ # Check if it's active/available
413
+ if db_url = AppConfig::Database::URL?
414
+ # Use the database connection
415
+ connect_to_database(db_url)
416
+ end
417
+ ```
418
+
419
+ This hierarchical approach makes configuration organization intuitive while
420
+ maintaining clear access patterns through Ruby's constant resolution mechanism.
421
+
422
+ #### Validation and Checks
423
+
424
+ ConstConf provides powerful validation capabilities through custom check blocks
425
+ that allow you to enforce business logic and data integrity rules. These checks
426
+ are evaluated during configuration resolution and appear in the view output
427
+ with visual indicators.
428
+
429
+ ```ruby
430
+ require 'const_conf'
431
+
432
+ module AppConfig
433
+ include ConstConf
434
+
435
+ description 'Application Configuration'
436
+ prefix '' # Use empty prefix for flat environment variable names
437
+
438
+ PORT = set do # from ENV['PORT']
439
+ description 'Server port'
440
+ default 3000
441
+ decode(&:to_i)
442
+ check { value >= 1024 } # Port must be >= 1024
443
+ end
444
+
445
+ HOST = set do # from ENV['APP_HOST']
446
+ description 'Host name'
447
+ prefix 'APP'
448
+ required { !Rails.env.test? }
449
+ check -> setting { setting.value.present? } # Must not be blank
450
+ end
451
+ end
452
+ ```
453
+
454
+ The output of `AppConfig.view` is:
455
+
456
+ ```
457
+ AppConfig # Application Configuration
458
+ ├─ prefix ""
459
+ ├─ 2 settings
460
+ ├─ AppConfig::PORT # Server port
461
+ │ ├─ prefix ""
462
+ │ ├─ env var name PORT
463
+ │ ├─ env var (orig.) nil
464
+ │ ├─ default 3000
465
+ │ ├─ value 3000
466
+ │ ├─ sensitive ⚪
467
+ │ ├─ required ⚪
468
+ │ ├─ configured ⚪
469
+ │ ├─ ignored ⚪
470
+ │ ├─ active 🟢
471
+ │ ├─ decoding ⚙️
472
+ │ └─ checked ✅
473
+ └─ AppConfig::HOST # Host name
474
+ ├─ prefix "APP"
475
+ ├─ env var name APP_HOST
476
+ ├─ env var (orig.) "www.example.com"
477
+ ├─ default nil
478
+ ├─ value "www.example.com"
479
+ ├─ sensitive ⚪
480
+ ├─ required 🔴
481
+ ├─ configured 🔧
482
+ ├─ ignored ⚪
483
+ ├─ active 🟢
484
+ ├─ decoding ⚪
485
+ └─ checked ✅
486
+ ```
487
+
488
+ ##### **How Validation Works:**
489
+
490
+ - **PORT**: The `check { value >= 1024 }` ensures that if a PORT environment
491
+ variable is set, it must be at least 1024 (a privileged port range)
492
+ - **HOST**: The `required { !Rails.env.test? }` makes this setting required
493
+ only in non-test environments, and the check validates that the value isn't
494
+ blank
495
+
496
+ The validation system ensures that your configuration meets both technical
497
+ requirements (like port ranges) and business rules (like non-empty hostnames),
498
+ making it much harder to deploy applications with invalid configurations.
499
+
500
+ **When validations fail during confirmation, ConstConf raises specific exceptions:**
501
+
502
+ - `ConstConf::RequiredValueNotConfigured` for missing required values
503
+ - `ConstConf::SettingCheckFailed` for failed custom checks
504
+ - These errors are raised immediately during configuration loading to prevent
505
+ runtime issues
506
+
507
+ #### Complex Settings Examples
508
+
509
+ ```ruby
510
+ require 'const_conf'
511
+
512
+ module AppConfig
513
+ include ConstConf
514
+
515
+ description 'Application Configuration'
516
+
517
+ # Version validation
518
+ REVISION = set do # from ENV['REVISION']
519
+ description 'Current software revision'
520
+ prefix ''
521
+ required { Rails.env.production? }
522
+ check { value.blank? || value.to_s =~ /\A\h{7}\z/ }
523
+ end
524
+
525
+ # Host validation with regex
526
+ HOST = set do # from ENV['APP_CONFIG_HOST']
527
+ description 'HOST name the application can be reached under'
528
+ required { Rails.env.production? }
529
+ check { value.blank? || value =~ /\A[a-z\-]+\.[a-z\-\.]+\z/ && value.size <= 253 }
530
+ end
531
+
532
+ # Multi-value validation
533
+ HOSTS_ALLOWED = set do # from ENV['APP_CONFIG_HOSTS_ALLOWED']
534
+ description 'Connections under these hostnames are allowed in Rails.'
535
+ default ''
536
+ decode { it.split(?,).map(&:strip) }
537
+ check { value.all? { |host| host =~ /\A[a-z\-]+\.[a-z\-\.]+\z/ && host.size <= 253 } }
538
+ end
539
+
540
+ # Sensitive configuration with validation
541
+ GITHUB_PERSONAL_ACCESS_TOKEN = set do # from ENV['GITHUB_PERSONAL_ACCESS_TOKEN']
542
+ description 'GitHub Personal Access Token for repo access'
543
+ prefix ''
544
+ required { !Rails.env.test? }
545
+ sensitive true
546
+ check { value.to_s =~ /\Aghp_[A-Za-z0-9]{36}\z/ }
547
+ end
548
+
549
+ # URI validation
550
+ REDIS_URL = set do # from ENV['REDIS_URL']
551
+ description 'Redis server URL'
552
+ prefix ''
553
+ default 'redis://localhost:6379/1'
554
+ sensitive true
555
+ check { URI.parse(value).scheme == 'redis' rescue false }
556
+ end
557
+ end
558
+ ```
559
+
560
+ The output of `AppConfig.view` is:
561
+
562
+ ```
563
+ AppConfig # Application Configuration
564
+ ├─ prefix "APP_CONFIG"
565
+ ├─ 5 settings
566
+ ├─ AppConfig::REVISION # Current software revision
567
+ │ ├─ prefix ""
568
+ │ ├─ env var name REVISION
569
+ │ ├─ env var (orig.) "b781318"
570
+ │ ├─ default nil
571
+ │ ├─ value "b781318"
572
+ │ ├─ sensitive ⚪
573
+ │ ├─ required ⚪
574
+ │ ├─ configured 🔧
575
+ │ ├─ ignored ⚪
576
+ │ ├─ active 🟢
577
+ │ ├─ decoding ⚪
578
+ │ └─ checked ✅
579
+ ├─ AppConfig::HOST # HOST name the application can be reached under
580
+ │ ├─ prefix "APP_CONFIG"
581
+ │ ├─ env var name APP_CONFIG_HOST
582
+ │ ├─ env var (orig.) "www.example.com"
583
+ │ ├─ default nil
584
+ │ ├─ value "www.example.com"
585
+ │ ├─ sensitive ⚪
586
+ │ ├─ required ⚪
587
+ │ ├─ configured 🔧
588
+ │ ├─ ignored ⚪
589
+ │ ├─ active 🟢
590
+ │ ├─ decoding ⚪
591
+ │ └─ checked ✅
592
+ ├─ AppConfig::HOSTS_ALLOWED # Connections under these hostnames are allowed in Rails.
593
+ │ ├─ prefix "APP_CONFIG"
594
+ │ ├─ env var name APP_CONFIG_HOSTS_ALLOWED
595
+ │ ├─ env var (orig.) "www.example.com,example.com…
596
+ │ ├─ default ""
597
+ │ ├─ value ["www.example.com", "example…
598
+ │ ├─ sensitive ⚪
599
+ │ ├─ required ⚪
600
+ │ ├─ configured 🔧
601
+ │ ├─ ignored ⚪
602
+ │ ├─ active 🟢
603
+ │ ├─ decoding ⚙️
604
+ │ └─ checked ✅
605
+ ├─ AppConfig::GITHUB_PERSONAL_ACCESS_TOKEN # GitHub Personal Access Token for repo access
606
+ │ ├─ prefix ""
607
+ │ ├─ env var name GITHUB_PERSONAL_ACCESS_TOKEN
608
+ │ ├─ env var (orig.) 🤫
609
+ │ ├─ default 🤫
610
+ │ ├─ value 🤫
611
+ │ ├─ sensitive 🔒
612
+ │ ├─ required 🔴
613
+ │ ├─ configured 🔧
614
+ │ ├─ ignored ⚪
615
+ │ ├─ active 🟢
616
+ │ ├─ decoding ⚪
617
+ │ └─ checked ✅
618
+ └─ AppConfig::REDIS_URL # Redis server URL
619
+ ├─ prefix ""
620
+ ├─ env var name REDIS_URL
621
+ ├─ env var (orig.) 🤫
622
+ ├─ default 🤫
623
+ ├─ value 🤫
624
+ ├─ sensitive 🔒
625
+ ├─ required ⚪
626
+ ├─ configured 🔧
627
+ ├─ ignored ⚪
628
+ ├─ active 🟢
629
+ ├─ decoding ⚪
630
+ └─ checked ✅
631
+ ```
632
+
633
+ ##### 1. Version Validation (`REVISION`)
634
+
635
+ ```ruby
636
+ REVISION = set do
637
+ description 'Current software revision'
638
+ prefix ''
639
+ required { Rails.env.production? }
640
+ check { value.blank? || value.to_s =~ /\A\h{7}\z/ }
641
+ end
642
+ ```
643
+
644
+ **Explanation:**
645
+
646
+ - **Purpose**: Validates that the revision is either blank (not set) or a valid
647
+ 7-character hexadecimal string
648
+ - **Conditional Required**: Only required in production environment
649
+ - **Validation Logic**: Uses regex `/\A\h{7}\z/` to match exactly 7 hexadecimal
650
+ characters (0-9, a-f)
651
+ - **Indicator**: Shows `🔴` when required and not satisfied, `✅` when valid
652
+
653
+ ##### 2. Host Validation with Regex (`HOST`)
654
+
655
+ ```ruby
656
+ HOST = set do
657
+ description 'HOST name the application can be reached under'
658
+ required { Rails.env.production? }
659
+ check { value.blank? || value =~ /\A[a-z\-]+\.[a-z\-\.]+\z/ && value.size <= 253 }
660
+ end
661
+ ```
662
+
663
+ **Explanation:**
664
+
665
+ - **Purpose**: Validates domain names with proper format and length constraints
666
+ - **Conditional Required**: Production-only requirement
667
+ - **Regex Pattern**:
668
+ - `/\A[a-z\-]+\.[a-z\-\.]+\z/` - Matches lowercase letters, hyphens, dots in
669
+ hostnames
670
+ - Ensures valid DNS format (e.g., "example.com")
671
+ - **Length Check**: Maximum 253 characters per RFC 1035
672
+ - **Indicator**: Shows validation status with visual cues
673
+
674
+ ##### 3. Multi-value Validation (`HOSTS_ALLOWED`)
675
+
676
+ ```ruby
677
+ HOSTS_ALLOWED = set do
678
+ description 'Connections under these hostnames are allowed in Rails.'
679
+ default ''
680
+ decode { it.split(?,).map(&:strip) }
681
+ check { value.all? { |host| host =~ /\A[a-z\-]+\.[a-z\-\.]+\z/ && host.size <= 253 } }
682
+ end
683
+ ```
684
+
685
+ **Explanation:**
686
+
687
+ - **Purpose**: Validates a comma-separated list of hostnames for allowed
688
+ connections
689
+ - **Default Value**: Empty string (no hosts allowed by default)
690
+ - **Decoding Logic**: Splits on commas and strips whitespace from each hostname
691
+ - **Validation**: Ensures ALL hosts in the list pass the same regex validation
692
+ as single hosts
693
+ - **Indicator**: Shows `⚙️` for decoding, `✅` for valid multi-value
694
+
695
+ ##### 4. Sensitive Configuration with Validation (`GITHUB_PERSONAL_ACCESS_TOKEN`)
696
+
697
+ ```ruby
698
+ GITHUB_PERSONAL_ACCESS_TOKEN = set do
699
+ description 'GitHub Personal Access Token for repo access'
700
+ prefix ''
701
+ required { !Rails.env.test? }
702
+ sensitive true
703
+ check { value.to_s =~ /\Aghp_[A-Za-z0-9]{36}\z/ }
704
+ end
705
+ ```
706
+
707
+ **Explanation:**
708
+
709
+ - **Purpose**: Validates GitHub personal access tokens with strict format
710
+ requirements
711
+ - **Conditional Required**: Not required in test environment
712
+ - **Sensitive Flag**: Masks the actual value in views (`🤫`)
713
+ - **Token Validation**:
714
+ - Must start with `ghp_`
715
+ - Followed by exactly 36 alphanumeric characters
716
+ - Matches GitHub's token format specification
717
+ - **Indicator**: Shows both `🔒` (sensitive) and `✅` (validated)
718
+
719
+ ##### 5. URI Validation (`REDIS_URL`)
720
+
721
+ ```ruby
722
+ REDIS_URL = set do
723
+ description 'Redis server URL'
724
+ prefix ''
725
+ default 'redis://localhost:6379/1'
726
+ sensitive true
727
+ check { URI.parse(value).scheme == 'redis' rescue false }
728
+ end
729
+ ```
730
+
731
+ **Explanation:**
732
+
733
+ - **Purpose**: Validates Redis connection URLs are properly formatted
734
+ - **Default Value**: Safe localhost configuration
735
+ - **Sensitive Flag**: Masks the URL in views
736
+ - **Validation Logic**: Attempts to parse as URI and checks for 'redis' scheme
737
+ - **Error Handling**: Uses `rescue false` to gracefully handle invalid URLs
738
+ - **Indicator**: Shows `✅` when valid, handles malformed inputs gracefully
739
+
740
+ ##### Key Technical Details
741
+
742
+ **Conditional Validation**: The use of Procs like `{ Rails.env.production? }`
743
+ allows dynamic validation rules based on runtime conditions.
744
+
745
+ **Safe Error Handling**: The URI parsing example demonstrates proper error
746
+ handling with rescue clauses to prevent configuration failures due to malformed
747
+ inputs.
748
+
749
+ **Regex Patterns**: All validations use precise regex patterns that match RFC
750
+ standards or specific format requirements, ensuring data integrity.
751
+
752
+ These examples showcase how ConstConf can handle complex business logic while
753
+ maintaining clean, readable configuration definitions. The combination of
754
+ conditional requirements, multi-value validation, and sensitive data protection
755
+ makes it suitable for production environments with strict security
756
+ requirements.
757
+
758
+ ## Rails Integration
759
+
760
+ ConstConf automatically integrates with Rails:
761
+
762
+ - Configuration is reloaded when the application prepares configuration
763
+ - Works seamlessly with Rails environment variables
764
+
765
+ ## Debugging and Inspection
766
+
767
+ View configuration hierarchies:
768
+
769
+ ```ruby
770
+ # Show all configuration settings
771
+ AppConfig.view
772
+
773
+ # Show specific setting details
774
+ AppConfig::DATABASE_URL!.view
775
+ ```
776
+
777
+ ## Download
778
+
779
+ The homepage of this library is located at
780
+
781
+ * https://github.com/flori/const_conf
782
+
783
+ ## Author
784
+
785
+ **ConstConf** was written by Florian Frank [Florian Frank](mailto:flori@ping.de)
786
+
787
+ ## License
788
+
789
+ [MIT License](./LICENSE)