Module: Lich::Common::Settings
- Defined in:
- documented/common/settings.rb
Overview
Provides configuration settings for the Lich application. This module handles logging, settings management, and data persistence.
Defined Under Namespace
Classes: CircularReferenceError
Constant Summary collapse
- LOG_LEVEL_NONE =
Logging Configuration
0- LOG_LEVEL_ERROR =
1- LOG_LEVEL_INFO =
2- LOG_LEVEL_DEBUG =
3- DEFAULT_SCOPE =
":".freeze
- @@log_level =
Default: logging disabled
LOG_LEVEL_NONE- @@log_prefix =
"[SettingsModule]".freeze
Class Method Summary collapse
-
.[](name) ⇒ Object
Retrieves a setting by name for the current script and scope.
-
.[]=(name, value) ⇒ nil
Sets a value for a setting by name for the current script and scope.
- ._log(level, prefix, message_proc) ⇒ Object
-
._reattach_live!(proxy) ⇒ Boolean
Reattaches the live proxy to the current settings context.
-
.auto ⇒ nil
Retrieves the auto configuration (legacy deprecated no-op).
-
.auto=(_val) ⇒ nil
Sets the auto configuration (legacy deprecated no-op).
-
.autoload ⇒ nil
Autoloads settings (legacy deprecated no-op).
-
.clear ⇒ nil
Clears all settings (legacy deprecated no-op).
-
.container?(value) ⇒ Boolean
Checks if the given value is a container (Hash or Array).
-
.current_script_settings(scope = DEFAULT_SCOPE) ⇒ Object
Retrieves the current settings for the active script and scope.
-
.get_log_level ⇒ Integer
Retrieves the current logging level for the Settings module.
-
.get_scoped_setting(scope_string, key_name) ⇒ Object
Retrieves a scoped setting by key name.
-
.get_value_from_container(container, key) ⇒ Object
Retrieves a value from a container (Hash or Array) by key.
-
.load ⇒ Object
Loads the current settings (legacy, aliasing to refresh_data).
-
.method_missing(method, *args, &block) ⇒ Object
Handles missing methods for the Settings module.
-
.navigate_to_path(create_missing = true, scope = DEFAULT_SCOPE) ⇒ Array
Navigates to the specified path within the current settings context.
-
.refresh_data(scope = DEFAULT_SCOPE) ⇒ Object
Refreshes the settings data for the current script and scope.
-
.reset_path_and_return(value) ⇒ Object
Resets the path navigator and returns the specified value.
-
.respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Checks if the Settings module responds to a missing method.
-
.root_proxy_for(scope, script_name: Script.current.name) ⇒ SettingsProxy
Retrieves the root proxy for the given scope and script name.
-
.save ⇒ Symbol
Saves the current settings (legacy no-op).
-
.save_all ⇒ nil
Saves all settings (legacy deprecated no-op).
-
.save_proxy_changes(proxy) ⇒ nil
Saves changes made to the given proxy back to the database.
-
.save_to_database(data_to_save, scope = DEFAULT_SCOPE) ⇒ nil
Saves the specified data to the database for the current script and scope.
-
.set_log_level(level) ⇒ Integer
Sets the logging level for the Settings module.
-
.set_script_settings(scope = DEFAULT_SCOPE, name, value) ⇒ nil
Sets a specific setting for the current script and scope.
-
.to_h(scope = DEFAULT_SCOPE) ⇒ Hash
Converts the current settings to a hash representation (legacy, aliasing to to_hash).
-
.to_hash(scope = DEFAULT_SCOPE) ⇒ Hash
Converts the current settings to a hash representation.
-
.wrap_value_if_container(value, scope, path_array) ⇒ Object
Wraps a value in a proxy if it is a container (Hash or Array).
Class Method Details
.[](name) ⇒ Object
Retrieves a setting by name for the current script and scope.
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'documented/common/settings.rb', line 492 def self.[](name) scope_to_use = DEFAULT_SCOPE _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[]: name: #{name.inspect}, current_path: #{@path_navigator.path.inspect}, safe_nav: #{@safe_navigation_active}" }) if @path_navigator.path.empty? data_for_scope = current_script_settings(scope_to_use) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[] (top-level): data_for_scope (DUP) (object_id: #{data_for_scope.object_id}): #{data_for_scope.inspect}" }) value = get_value_from_container(data_for_scope, name) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[] (top-level): value for '#{name}': #{value.inspect}" }) if value.nil? && !data_for_scope.is_a?(Array) && (!data_for_scope.is_a?(Hash) || !data_for_scope.key?(name)) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.[] (top-level): Key '#{name}' not found or value is nil. Activating safe_navigation." }) @safe_navigation_active = true end return reset_path_and_return(wrap_value_if_container(value, scope_to_use, [name])) else current_target, _ = navigate_to_path(false, scope_to_use) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[] (path-based): current_target: #{current_target.inspect}" }) value = get_value_from_container(current_target, name) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[] (path-based): value for '#{name}': #{value.inspect}" }) new_path = @path_navigator.path + [name] if value.nil? && !current_target.is_a?(Array) && (!current_target.is_a?(Hash) || !current_target.key?(name)) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.[] (path-based): Key '#{name}' not found or value is nil in path. Activating safe_navigation." }) @safe_navigation_active = true end return reset_path_and_return(wrap_value_if_container(value, scope_to_use, new_path)) end end |
.[]=(name, value) ⇒ nil
Sets a value for a setting by name for the current script and scope.
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 |
# File 'documented/common/settings.rb', line 526 def self.[]=(name, value) scope_to_use = DEFAULT_SCOPE _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "Settings.[]=: name: #{name.inspect}, value: #{value.inspect}, current_path: #{@path_navigator.path.inspect}" }) @safe_navigation_active = false # Reset safe navigation on assignment if @path_navigator.path.empty? set_script_settings(scope_to_use, name, value) else target, root_settings = navigate_to_path(true, scope_to_use) if target && (target.is_a?(Hash) || target.is_a?(Array)) actual_value = value.is_a?(SettingsProxy) ? unwrap_proxies(value) : value target[name] = actual_value save_to_database(root_settings, scope_to_use) reset_path_and_return(value) else _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "Settings.[]=: Cannot assign to non-container or nil target at path #{@path_navigator.path.inspect}" }) reset_path_and_return(nil) end end end |
._log(level, prefix, message_proc) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'documented/common/settings.rb', line 62 def self._log(level, prefix, ) return unless Lich.respond_to?(:log) return unless level <= @@log_level level_str = case level when LOG_LEVEL_ERROR then "[ERROR]" when LOG_LEVEL_INFO then "[INFO]" when LOG_LEVEL_DEBUG then "[DEBUG]" else "[UNKNOWN]" end begin = .call Lich.log("#{prefix} #{level_str} #{}") rescue => e Lich.log("#{prefix} [ERROR] Logging failed: #{e.} - Original message proc: #{.source_location if .respond_to?(:source_location)}") end end |
._reattach_live!(proxy) ⇒ Boolean
Reattaches the live proxy to the current settings context.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'documented/common/settings.rb', line 86 def self._reattach_live!(proxy) script_name = Script.current.name scope = proxy.scope path = proxy.path _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "reattach_live!: scope=#{scope.inspect} path=#{path.inspect}" }) @path_navigator.reset_path @path_navigator.set_path(path) live, _root = @path_navigator.navigate_to_path(script_name, true, scope) if live.nil? _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "reattach_live!: failed to resolve live target for path=#{path.inspect} scope=#{scope.inspect}" }) return false end # Swap the proxy onto the live object via SettingsProxy API (encapsulated) # Centralizes invariants/logging within SettingsProxy itself. proxy.rebind_to_live!(live) true end |
.auto ⇒ nil
Retrieves the auto configuration (legacy deprecated no-op).
664 665 666 667 668 |
# File 'documented/common/settings.rb', line 664 def self.auto Lich.deprecated('Settings.auto', 'not using, not applicable,', caller[0], fe_log: true) if Lich.respond_to?(:deprecated) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.auto called (legacy deprecated no-op)." }) nil end |
.auto=(_val) ⇒ nil
Sets the auto configuration (legacy deprecated no-op).
655 656 657 658 |
# File 'documented/common/settings.rb', line 655 def self.auto=(_val) Lich.deprecated('Settings.auto=(val)', 'not using, not applicable,', caller[0], fe_log: true) if Lich.respond_to?(:deprecated) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.auto= called (legacy deprecated no-op)." }) end |
.autoload ⇒ nil
Autoloads settings (legacy deprecated no-op).
674 675 676 677 678 |
# File 'documented/common/settings.rb', line 674 def self.autoload Lich.deprecated('Settings.autoload', 'not using, not applicable,', caller[0], fe_log: true) if Lich.respond_to?(:deprecated) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.autoload called (legacy deprecated no-op)." }) nil end |
.clear ⇒ nil
Clears all settings (legacy deprecated no-op).
644 645 646 647 648 |
# File 'documented/common/settings.rb', line 644 def self.clear Lich.deprecated('Settings.clear', 'not using, not applicable,', caller[0], fe_log: true) if Lich.respond_to?(:deprecated) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.clear called (legacy deprecated no-op)." }) nil end |
.container?(value) ⇒ Boolean
Checks if the given value is a container (Hash or Array).
119 120 121 |
# File 'documented/common/settings.rb', line 119 def self.container?(value) value.is_a?(Hash) || value.is_a?(Array) end |
.current_script_settings(scope = DEFAULT_SCOPE) ⇒ Object
Retrieves the current settings for the active script and scope.
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'documented/common/settings.rb', line 353 def self.current_script_settings(scope = DEFAULT_SCOPE) script_name = Script.current.name cache_key = "#{script_name || ""}::#{scope}" # Use an empty string if script_name is nil _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "current_script_settings: Request for scope: #{scope.inspect}, cache_key: #{cache_key}" }) cached_data = @settings_cache[cache_key] if cached_data _log(LOG_LEVEL_INFO, @@log_prefix, -> { "current_script_settings: Cache hit for #{cache_key} (object_id: #{cached_data.object_id}). Returning DUP: #{cached_data.inspect}" }) return cached_data.dup else _log(LOG_LEVEL_INFO, @@log_prefix, -> { "current_script_settings: Cache miss for #{cache_key}. Loading from DB." }) settings = @db_adapter.get_settings(script_name, scope) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "current_script_settings: Loaded from DB (object_id: #{settings.object_id}): #{settings.inspect}" }) @settings_cache[cache_key] = settings _log(LOG_LEVEL_INFO, @@log_prefix, -> { "current_script_settings: Stored in cache (object_id: #{@settings_cache[cache_key].object_id}). Returning DUP." }) return settings.dup end end |
.get_log_level ⇒ Integer
Retrieves the current logging level for the Settings module.
58 59 60 |
# File 'documented/common/settings.rb', line 58 def self.get_log_level @@log_level end |
.get_scoped_setting(scope_string, key_name) ⇒ Object
Retrieves a scoped setting by key name.
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
# File 'documented/common/settings.rb', line 553 def self.get_scoped_setting(scope_string, key_name) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "get_scoped_setting: scope: #{scope_string.inspect}, key: #{key_name.inspect}" }) data_for_scope = current_script_settings(scope_string) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "get_scoped_setting: data_for_scope (DUP) (object_id: #{data_for_scope.object_id}): #{data_for_scope.inspect}" }) value = get_value_from_container(data_for_scope, key_name) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "get_scoped_setting: value for '#{key_name}': #{value.inspect}" }) if value.nil? && key_name key_absent_in_hash = data_for_scope.is_a?(Hash) && !data_for_scope.key?(key_name) key_invalid_for_array = data_for_scope.is_a?(Array) && (!key_name.is_a?(Integer) || key_name < 0 || key_name >= data_for_scope.length) if key_absent_in_hash || key_invalid_for_array || (data_for_scope.nil? || (data_for_scope.is_a?(Hash) && data_for_scope.empty?)) _log(Settings::LOG_LEVEL_INFO, @@log_prefix, -> { "get_scoped_setting: Key '#{key_name}' not found in scope '#{scope_string}'. Value will be nil, supporting '|| default' idiom." }) end end wrap_value_if_container(value, scope_string, key_name ? [key_name] : []) end |
.get_value_from_container(container, key) ⇒ Object
Retrieves a value from a container (Hash or Array) by key.
717 718 719 720 721 722 723 724 725 726 727 728 |
# File 'documented/common/settings.rb', line 717 def self.get_value_from_container(container, key) if container.is_a?(Hash) container[key] elsif container.is_a?(Array) && key.is_a?(Integer) container[key] elsif container.is_a?(Array) && !key.is_a?(Integer) _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "get_value_from_container: Attempted to access Array with non-Integer key: #{key.inspect}" }) nil else nil end end |
.load ⇒ Object
Loads the current settings (legacy, aliasing to refresh_data).
615 616 617 618 |
# File 'documented/common/settings.rb', line 615 def self.load _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.load called (legacy, aliasing to refresh_data)." }) refresh_data end |
.method_missing(method, *args, &block) ⇒ Object
Handles missing methods for the Settings module.
687 688 689 690 691 692 693 694 695 696 697 698 699 |
# File 'documented/common/settings.rb', line 687 def self.method_missing(method, *args, &block) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "method_missing: method: #{method}, args: #{args.inspect}, path: #{@path_navigator.path.inspect}" }) if @safe_navigation_active && !@path_navigator.path.empty? if method.to_s.end_with?("=") _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "method_missing: Attempted assignment (#{method}) on a nil path due to safe navigation." }) return reset_path_and_return(nil) end return reset_path_and_return(nil) end _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "method_missing: Delegating to path_navigator: #{method}" }) @path_navigator.send(method, *args, &block) end |
.navigate_to_path(create_missing = true, scope = DEFAULT_SCOPE) ⇒ Array
Navigates to the specified path within the current settings context.
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'documented/common/settings.rb', line 428 def self.navigate_to_path(create_missing = true, scope = DEFAULT_SCOPE) root_for_scope = current_script_settings(scope) return [root_for_scope, root_for_scope] if @path_navigator.path.empty? target = root_for_scope @path_navigator.path.each do |key| if target.is_a?(Hash) && target.key?(key) target = target[key] elsif target.is_a?(Array) && key.is_a?(Integer) && key >= 0 && key < target.length target = target[key] elsif create_missing && (target.is_a?(Hash) || target.is_a?(Array)) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "navigate_to_path: Creating missing segment '#{key}' in DUPPED structure for scope #{scope.inspect}" }) new_node = key.is_a?(Integer) ? [] : {} if target.is_a?(Hash) target[key] = new_node elsif target.is_a?(Array) && key.is_a?(Integer) target[key] = new_node end target = new_node else return [nil, root_for_scope] end end [target, root_for_scope] end |
.refresh_data(scope = DEFAULT_SCOPE) ⇒ Object
Refreshes the settings data for the current script and scope.
405 406 407 408 409 410 411 |
# File 'documented/common/settings.rb', line 405 def self.refresh_data(scope = DEFAULT_SCOPE) script_name = Script.current.name cache_key = "#{script_name || ""}::#{scope}" # Use an empty string if script_name is nil _log(LOG_LEVEL_INFO, @@log_prefix, -> { "refresh_data: Deleting cache for scope: #{scope.inspect}, cache_key: #{cache_key}" }) @settings_cache.delete(cache_key) current_script_settings(scope) end |
.reset_path_and_return(value) ⇒ Object
Resets the path navigator and returns the specified value.
418 419 420 |
# File 'documented/common/settings.rb', line 418 def self.reset_path_and_return(value) @path_navigator.reset_path_and_return(value) end |
.respond_to_missing?(method_name, include_private = false) ⇒ Boolean
Checks if the Settings module responds to a missing method.
707 708 709 |
# File 'documented/common/settings.rb', line 707 def self.respond_to_missing?(method_name, include_private = false) @path_navigator.respond_to?(method_name, include_private) || super end |
.root_proxy_for(scope, script_name: Script.current.name) ⇒ SettingsProxy
Retrieves the root proxy for the given scope and script name.
161 162 163 164 165 166 167 168 169 |
# File 'documented/common/settings.rb', line 161 def self.root_proxy_for(scope, script_name: Script.current.name) raise ArgumentError, "scope must be a non-empty String" if scope.nil? || scope.to_s.strip.empty? script_name ||= "" cache_key = "#{script_name}::#{scope}" root = @settings_cache[cache_key] ||= @db_adapter.get_settings(script_name, scope) SettingsProxy.new(self, scope, [], root) end |
.save ⇒ Symbol
Saves the current settings (legacy no-op).
606 607 608 609 |
# File 'documented/common/settings.rb', line 606 def self.save _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.save called (legacy no-op)." }) :noop end |
.save_all ⇒ nil
Saves all settings (legacy deprecated no-op).
634 635 636 637 638 |
# File 'documented/common/settings.rb', line 634 def self.save_all Lich.deprecated('Settings.save_all', 'not using, not applicable,', caller[0], fe_log: true) if Lich.respond_to?(:deprecated) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.save_all called (legacy deprecated no-op)." }) nil end |
.save_proxy_changes(proxy) ⇒ nil
Saves changes made to the given proxy back to the database.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'documented/common/settings.rb', line 176 def self.save_proxy_changes(proxy) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Initiated for proxy.scope: #{proxy.scope.inspect}, proxy.path: #{proxy.path.inspect}, proxy.target_object_id: #{proxy.target.object_id}" }) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: proxy.target data: #{proxy.target.inspect}" }) path = proxy.path scope = proxy.scope script_name = Script.current.name cache_key = "#{script_name || ""}::#{scope}" # Local helper to keep cache in sync with the just-persisted root sync_cache = lambda do |root_obj| cached = @settings_cache[cache_key] if cached && !cached.equal?(root_obj) if cached.is_a?(Hash) && root_obj.is_a?(Hash) cached.replace(root_obj) elsif cached.is_a?(Array) && root_obj.is_a?(Array) cached.clear cached.concat(root_obj) else @settings_cache[cache_key] = root_obj end elsif cached.nil? @settings_cache[cache_key] = root_obj end _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Cache synchronized post-save for #{cache_key}" }) end # --- Refresh-before-save to prevent stale-cache overwrites --- fresh_root = @db_adapter.get_settings(script_name, scope) cached = @settings_cache[cache_key] if cached if cached.is_a?(Hash) && fresh_root.is_a?(Hash) cached.replace(fresh_root) current_root_for_scope = cached elsif cached.is_a?(Array) && fresh_root.is_a?(Array) cached.clear cached.concat(fresh_root) current_root_for_scope = cached else @settings_cache[cache_key] = fresh_root current_root_for_scope = fresh_root end _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_proxy_changes: Cache refreshed from DB for #{cache_key} (object_id: #{current_root_for_scope.object_id}): #{current_root_for_scope.inspect}" }) else @settings_cache[cache_key] = fresh_root current_root_for_scope = fresh_root _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_proxy_changes: Cache populated from DB for #{cache_key} (object_id: #{current_root_for_scope.object_id}): #{current_root_for_scope.inspect}" }) end # ------------------------------------------------------------- # EMPTY PATH → Save *current root* (not proxy.target). Also covers detached "view" proxies. if path.empty? _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Empty path; saving CURRENT ROOT for scope #{scope.inspect}" }) unless current_root_for_scope.is_a?(Hash) || current_root_for_scope.is_a?(Array) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_proxy_changes: Root not a container; initializing {} for scope #{scope.inspect}" }) current_root_for_scope = {} @settings_cache[cache_key] = current_root_for_scope end if proxy.respond_to?(:detached?) && proxy.detached? _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Proxy is detached (view); persisting current root without copying view target." }) save_to_database(current_root_for_scope, scope) sync_cache.call(current_root_for_scope) return nil end # Root identity drift: sync proxy.target into cached root if different objects (same-type containers). if !current_root_for_scope.equal?(proxy.target) if proxy.target.is_a?(Hash) && current_root_for_scope.is_a?(Hash) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Root identity mismatch (cache #{current_root_for_scope.object_id} vs proxy #{proxy.target.object_id}); copying via Hash#replace" }) current_root_for_scope.replace(proxy.target) elsif proxy.target.is_a?(Array) && current_root_for_scope.is_a?(Array) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Root identity mismatch (Array); copying elements" }) current_root_for_scope.clear current_root_for_scope.concat(proxy.target) else _log(LOG_LEVEL_WARN, @@log_prefix, -> { "save_proxy_changes: Root/target type mismatch; persisting current root only (root=#{current_root_for_scope.class}, target=#{proxy.target.class})." }) end end save_to_database(current_root_for_scope, scope) sync_cache.call(current_root_for_scope) return nil end _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: script_name: #{script_name.inspect}, cache_key: #{cache_key}" }) # From here on, we’re saving into a nested path. Ensure root is a container. unless current_root_for_scope.is_a?(Hash) || current_root_for_scope.is_a?(Array) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_proxy_changes: Root not a container; initializing {} for scope #{scope.inspect}" }) current_root_for_scope = {} @settings_cache[cache_key] = current_root_for_scope end parent_path = path[0...-1] leaf_key = path.last # Pre-navigation diagnostics _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Navigation preflight — parent_path=#{parent_path.inspect} (#{parent_path.map { |s| s.class }.inspect}), leaf_key=#{leaf_key.inspect} (#{leaf_key.class}), root_class=#{current_root_for_scope.class}" }) # Navigate **within the freshly-loaded root** (do NOT re-fetch via PathNavigator here). begin parent = current_root_for_scope parent_path.each_with_index do |seg, idx| next_seg = parent_path[idx + 1] if parent.is_a?(Hash) unless parent.key?(seg) parent[seg] = next_seg.is_a?(Integer) ? [] : {} _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Created #{parent[seg].class} at #{parent_path[0..idx].inspect} for scope #{scope.inspect}" }) end parent = parent[seg] elsif parent.is_a?(Array) unless seg.is_a?(Integer) && seg >= 0 _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Non-integer or negative index #{seg.inspect} for Array at #{parent_path[0..idx].inspect} in scope #{scope.inspect}" }) return nil end if seg >= parent.length (parent.length..seg).each { parent << nil } _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Extended Array to index #{seg} at #{parent_path[0..idx].inspect} for scope #{scope.inspect}" }) end parent[seg] ||= (next_seg.is_a?(Integer) ? [] : {}) parent = parent[seg] else _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Parent is not a container at #{parent_path[0..idx].inspect} (#{parent.class}) for scope #{scope.inspect}" }) return nil end end rescue => e _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Local navigation raised #{e.class}: #{e.}. "\ "scope=#{scope.inspect}, script_name=#{script_name.inspect}, cache_key=#{cache_key}, "\ "parent_path=#{parent_path.inspect}, leaf_key=#{leaf_key.inspect}" }) bt = (e.backtrace || [])[0, 5] _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Backtrace (top 5): #{bt.join(' | ')}" }) return nil end _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Navigated/created parent (object_id: #{parent.object_id}, class=#{parent.class}): #{parent.inspect}" }) if parent.is_a?(Hash) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Setting Hash key #{leaf_key.inspect} with proxy.target (object_id: #{proxy.target.object_id})" }) parent[leaf_key] = proxy.target elsif parent.is_a?(Array) && leaf_key.is_a?(Integer) if leaf_key < 0 _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Negative array index #{leaf_key} not supported at path #{path.inspect} in scope #{scope.inspect}" }) return nil end if leaf_key >= parent.length (parent.length..leaf_key).each { parent << nil } _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Extended Array to index #{leaf_key} for parent at path #{parent_path.inspect}" }) end parent[leaf_key] = proxy.target _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: Set Array index #{leaf_key} with proxy.target (object_id: #{proxy.target.object_id})" }) else _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_proxy_changes: Cannot set value at path #{path.inspect} in scope #{scope.inspect}; "\ "parent_class=#{parent.class}, leaf_key_class=#{leaf_key.class}" }) return nil end _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_proxy_changes: root after update (object_id: #{current_root_for_scope.object_id}): #{current_root_for_scope.inspect}" }) save_to_database(current_root_for_scope, scope) sync_cache.call(current_root_for_scope) end |
.save_to_database(data_to_save, scope = DEFAULT_SCOPE) ⇒ nil
Saves the specified data to the database for the current script and scope.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
# File 'documented/common/settings.rb', line 378 def self.save_to_database(data_to_save, scope = DEFAULT_SCOPE) script_name = Script.current.name if script_name.nil? || script_name.empty? _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "save_to_database: Aborting save. Script.current.name is nil or empty. Scope: #{scope.inspect}. Data will NOT be persisted." }) return nil # Explicitly return nil end cache_key = "#{script_name}::#{scope}" # script_name is guaranteed to be non-nil/non-empty here _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_to_database: Saving for script: '#{script_name}', scope: #{scope.inspect}, cache_key: #{cache_key}" }) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_to_database: Data BEFORE unwrap_proxies (object_id: #{data_to_save.object_id}): #{data_to_save.inspect}" }) unwrapped_settings = unwrap_proxies(data_to_save) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "save_to_database: Data AFTER unwrap_proxies (object_id: #{unwrapped_settings.object_id}): #{unwrapped_settings.inspect}" }) @db_adapter.save_settings(script_name, unwrapped_settings, scope) _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_to_database: Data saved to DB for script '#{script_name}'." }) @settings_cache[cache_key] = unwrapped_settings _log(LOG_LEVEL_INFO, @@log_prefix, -> { "save_to_database: Cache updated for #{cache_key} with saved data (object_id: #{@settings_cache[cache_key].object_id})." }) end |
.set_log_level(level) ⇒ Integer
Sets the logging level for the Settings module.
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'documented/common/settings.rb', line 43 def self.set_log_level(level) numeric_level = case level when :none, LOG_LEVEL_NONE then LOG_LEVEL_NONE when :error, LOG_LEVEL_ERROR then LOG_LEVEL_ERROR when :info, LOG_LEVEL_INFO then LOG_LEVEL_INFO when :debug, LOG_LEVEL_DEBUG then LOG_LEVEL_DEBUG else _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "Invalid log level specified: #{level.inspect}. Defaulting to NONE." }) LOG_LEVEL_NONE end @@log_level = numeric_level end |
.set_script_settings(scope = DEFAULT_SCOPE, name, value) ⇒ nil
Sets a specific setting for the current script and scope.
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'documented/common/settings.rb', line 461 def self.set_script_settings(scope = DEFAULT_SCOPE, name, value) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "set_script_settings: scope: #{scope.inspect}, name: #{name.inspect}, value: #{value.inspect}, current_path: #{@path_navigator.path.inspect}" }) unwrapped_value = unwrap_proxies(value) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "set_script_settings: unwrapped_value: #{unwrapped_value.inspect}" }) current_root = current_script_settings(scope) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "set_script_settings: current_root (DUP) for scope #{scope.inspect} (object_id: #{current_root.object_id}): #{current_root.inspect}" }) if @path_navigator.path.empty? _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "set_script_settings: Path is empty. Setting '#{name}' on current_root." }) current_root[name] = unwrapped_value save_to_database(current_root, scope) else if !@path_navigator.path.empty? _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "set_script_settings: WARNING: Called with non-empty path_navigator path: #{@path_navigator.path.inspect}. This is unusual for Char/GameSettings direct assignment." }) end if current_root.is_a?(Hash) current_root[name] = unwrapped_value save_to_database(current_root, scope) else _log(LOG_LEVEL_ERROR, @@log_prefix, -> { "set_script_settings: current_root for scope #{scope.inspect} is not a Hash. Cannot set key '#{name}'. Root class: #{current_root.class}" }) end end reset_path_and_return(value) end |
.to_h(scope = DEFAULT_SCOPE) ⇒ Hash
Converts the current settings to a hash representation (legacy, aliasing to to_hash).
625 626 627 628 |
# File 'documented/common/settings.rb', line 625 def self.to_h(scope = DEFAULT_SCOPE) # Added scope to match to_hash for consistency if used directly _log(LOG_LEVEL_INFO, @@log_prefix, -> { "Settings.to_h called (legacy, aliasing to to_hash)." }) self.to_hash(scope) end |
.to_hash(scope = DEFAULT_SCOPE) ⇒ Hash
Converts the current settings to a hash representation.
594 595 596 597 598 599 600 |
# File 'documented/common/settings.rb', line 594 def self.to_hash(scope = DEFAULT_SCOPE) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "to_hash: scope: #{scope.inspect}" }) data = current_script_settings(scope) unwrapped_data = unwrap_proxies(data) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "to_hash: Returning unwrapped data (snapshot): #{unwrapped_data.inspect}" }) return unwrapped_data end |
.wrap_value_if_container(value, scope, path_array) ⇒ Object
Wraps a value in a proxy if it is a container (Hash or Array).
578 579 580 581 582 583 584 585 586 587 |
# File 'documented/common/settings.rb', line 578 def self.wrap_value_if_container(value, scope, path_array) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "wrap_value_if_container: value_class: #{value.class}, scope: #{scope.inspect}, path: #{path_array.inspect}" }) if container?(value) proxy = SettingsProxy.new(self, scope, path_array, value) _log(LOG_LEVEL_DEBUG, @@log_prefix, -> { "wrap_value_if_container: Wrapped in proxy: #{proxy.inspect}" }) return proxy else return value end end |