@@ -24,23 +24,39 @@ class ConfigLoader
24
24
normalize_headers : true
25
25
} . freeze
26
26
27
+ SILENCE_CONFIG_LOADER_MESSAGES = ENV . fetch (
28
+ "HOOKS_SILENCE_CONFIG_LOADER_MESSAGES" , "false"
29
+ ) . downcase == "true" . freeze
30
+
27
31
# Load and merge configuration from various sources
28
32
#
29
33
# @param config_path [String, Hash] Path to config file or config hash
30
34
# @return [Hash] Merged configuration
31
35
def self . load ( config_path : nil )
32
36
config = DEFAULT_CONFIG . dup
37
+ overrides = [ ]
33
38
34
39
# Load from file if path provided
35
40
if config_path . is_a? ( String ) && File . exist? ( config_path )
36
41
file_config = load_config_file ( config_path )
37
- config . merge! ( file_config ) if file_config
38
- elsif config_path . is_a? ( Hash )
39
- config . merge! ( config_path )
42
+ if file_config
43
+ overrides << "file config"
44
+ config . merge! ( file_config )
45
+ end
40
46
end
41
47
42
- # Override with environment variables
43
- config . merge! ( load_env_config )
48
+ # Override with environment variables (before programmatic config)
49
+ env_config = load_env_config
50
+ if env_config . any?
51
+ overrides << "environment variables"
52
+ config . merge! ( env_config )
53
+ end
54
+
55
+ # Programmatic config has highest priority
56
+ if config_path . is_a? ( Hash )
57
+ overrides << "programmatic config"
58
+ config . merge! ( config_path )
59
+ end
44
60
45
61
# Convert string keys to symbols for consistency
46
62
config = symbolize_keys ( config )
@@ -51,6 +67,11 @@ def self.load(config_path: nil)
51
67
config [ :production ] = false
52
68
end
53
69
70
+ # Log overrides if any were made
71
+ if overrides . any?
72
+ puts "INFO: Configuration overrides applied from: #{ overrides . join ( ', ' ) } " unless SILENCE_CONFIG_LOADER_MESSAGES
73
+ end
74
+
54
75
return config
55
76
end
56
77
@@ -93,8 +114,9 @@ def self.load_config_file(file_path)
93
114
end
94
115
95
116
result
96
- rescue => _e
97
- # In production, we'd log this error
117
+ rescue => e
118
+ # Log this error with meaningful information
119
+ puts "ERROR: Failed to load config file '#{ file_path } ': #{ e . message } " unless SILENCE_CONFIG_LOADER_MESSAGES
98
120
nil
99
121
end
100
122
@@ -105,26 +127,35 @@ def self.load_env_config
105
127
env_config = { }
106
128
107
129
env_mappings = {
130
+ "HOOKS_HANDLER_DIR" => :handler_dir ,
108
131
"HOOKS_HANDLER_PLUGIN_DIR" => :handler_plugin_dir ,
109
132
"HOOKS_AUTH_PLUGIN_DIR" => :auth_plugin_dir ,
133
+ "HOOKS_LIFECYCLE_PLUGIN_DIR" => :lifecycle_plugin_dir ,
134
+ "HOOKS_INSTRUMENTS_PLUGIN_DIR" => :instruments_plugin_dir ,
110
135
"HOOKS_LOG_LEVEL" => :log_level ,
111
136
"HOOKS_REQUEST_LIMIT" => :request_limit ,
112
137
"HOOKS_REQUEST_TIMEOUT" => :request_timeout ,
113
138
"HOOKS_ROOT_PATH" => :root_path ,
114
139
"HOOKS_HEALTH_PATH" => :health_path ,
115
140
"HOOKS_VERSION_PATH" => :version_path ,
116
141
"HOOKS_ENVIRONMENT" => :environment ,
117
- "HOOKS_ENDPOINTS_DIR" => :endpoints_dir
142
+ "HOOKS_ENDPOINTS_DIR" => :endpoints_dir ,
143
+ "HOOKS_USE_CATCHALL_ROUTE" => :use_catchall_route ,
144
+ "HOOKS_SYMBOLIZE_PAYLOAD" => :symbolize_payload ,
145
+ "HOOKS_NORMALIZE_HEADERS" => :normalize_headers
118
146
}
119
147
120
148
env_mappings . each do |env_key , config_key |
121
149
value = ENV [ env_key ]
122
150
next unless value
123
151
124
- # Convert numeric values
152
+ # Convert values to appropriate types
125
153
case config_key
126
154
when :request_limit , :request_timeout
127
155
env_config [ config_key ] = value . to_i
156
+ when :use_catchall_route , :symbolize_payload , :normalize_headers
157
+ # Convert string to boolean
158
+ env_config [ config_key ] = %w[ true 1 yes on ] . include? ( value . downcase )
128
159
else
129
160
env_config [ config_key ] = value
130
161
end
0 commit comments