Class: Lich::Common::SettingsProxy
- Inherits:
-
Object
- Object
- Lich::Common::SettingsProxy
- Defined in:
- documented/common/settings/settings_proxy.rb
Overview
SettingsProxy is defined here but relies on Settings module being fully defined first, especially Settings._log. The actual require_relative for settings_proxy.rb is now at the end of settings.rb. Proxy class for managing settings with a target object. This class allows for delegation of method calls to a target object, while providing additional functionality such as logging and handling detached states.
Constant Summary collapse
- LOG_PREFIX =
"[SettingsProxy]".freeze
- NON_DESTRUCTIVE_METHODS =
[ :+, :-, :&, :|, :*, :all?, :any?, :assoc, :at, :bsearch, :bsearch_index, :chunk, :chunk_while, :collect, :collect_concat, :compact, :compare_by_identity?, :count, :cycle, :default, :default_proc, :detect, :dig, :drop, :drop_while, :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :empty?, :entries, :except, :fetch, :fetch_values, :filter, :find, :find_all, :find_index, :first, :flat_map, :flatten, :frozen?, :grep, :grep_v, :group_by, :has_value?, :include?, :inject, :invert, :join, :key, :keys, :last, :lazy, :length, :map, :max, :max_by, :member?, :merge, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :pack, :partition, :permutation, :product, :rassoc, :reduce, :reject, :reverse, :rotate, :sample, :select, :shuffle, :size, :slice, :slice_after, :slice_before, :slice_when, :sort, :sort_by, :sum, :take, :take_while, :to_a, :to_h, :to_proc, :transform_keys, :transform_values, :uniq, :values, :values_at, :zip ].freeze
- NON_DESTRUCTIVE_CONTAINER_VIEWS =
Subset of non-destructive methods that return container “views”
[ :map, :collect, :select, :filter, :reject, :find_all, :grep, :grep_v, :sort, :sort_by, :uniq, :compact, :flatten, :slice, :take, :drop, :values ].freeze
Instance Attribute Summary collapse
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
-
#target ⇒ Object
readonly
Returns the value of attribute target.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Retrieves a value from the target using the specified key.
-
#[]=(key, value) ⇒ Object
Sets a value in the target using the specified key.
-
#binary_op(operator, other) ⇒ Object
Performs a binary operation with the target and another value.
-
#detached? ⇒ Boolean
Minimal change: expose detached? status Checks if the proxy is in a detached state.
-
#each {|Object| ... } ⇒ self
Minimal change: items yielded from #each are “views” over the container.
- #handle_method_result(result) ⇒ Object
-
#handle_non_destructive_result(method, result) ⇒ Object
Minimal change: keep path (not []), and tag view proxies as detached.
-
#hash ⇒ Integer
Returns the hash of the target object.
-
#initialize(settings_module, scope, path, target, detached: false) ⇒ SettingsProxy
constructor
Minimal change: add detached flag (default false) Initializes a new SettingsProxy instance.
-
#inspect ⇒ String
Updated inspect method to show the target’s inspect string Returns a string representation of the proxy’s target.
- #instance_of?(klass) ⇒ Boolean
- #is_a?(klass) ⇒ Boolean
- #kind_of?(klass) ⇒ Boolean
-
#method_missing(method, *args, &block) ⇒ Object
Handles calls to methods that are not explicitly defined.
-
#nil? ⇒ Boolean
Checks if the target is nil.
- #pretty_print(pp) ⇒ Object
-
#proxy_details ⇒ String
New method to show the proxy’s internal details Returns a string with the proxy’s internal details.
- #respond_to?(method, include_private = false) ⇒ Boolean
-
#respond_to_missing?(method, include_private = false) ⇒ Boolean
Checks if the target responds to a method, including private methods.
-
#to_a ⇒ Object
Collection conversions.
- #to_ary ⇒ Object
- #to_c ⇒ Object
-
#to_enum(*args, &block) ⇒ Object
Special case for to_enum.
- #to_f ⇒ Object
- #to_h ⇒ Object
- #to_hash ⇒ Object
-
#to_i ⇒ Integer
Numeric conversions Converts the target to an integer.
- #to_int ⇒ Object
- #to_io ⇒ Object
-
#to_json(*args) ⇒ String
added 20250620 for JSON.pretty_generate Converts the target to JSON format.
- #to_path ⇒ Object
-
#to_proc ⇒ Object
Other common conversions.
- #to_r ⇒ Object
-
#to_s ⇒ String
String conversions Converts the target to a string.
-
#to_str ⇒ String
Converts the target to a string, strict version.
- #to_sym ⇒ Object
Constructor Details
#initialize(settings_module, scope, path, target, detached: false) ⇒ SettingsProxy
Minimal change: add detached flag (default false) Initializes a new SettingsProxy instance.
25 26 27 28 29 30 31 32 |
# File 'documented/common/settings/settings_proxy.rb', line 25 def initialize(settings_module, scope, path, target, detached: false) @settings_module = settings_module # This should be the Settings module itself @scope = scope @path = path.dup @target = target @detached = detached @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "INIT scope: #{@scope.inspect}, path: #{@path.inspect}, target_class: #{@target.class}, target_object_id: #{@target.object_id}, detached: #{@detached}" }) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
Handles calls to methods that are not explicitly defined.
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'documented/common/settings/settings_proxy.rb', line 323 def method_missing(method, *args, &block) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL scope: #{@scope.inspect}, path: #{@path.inspect}, method: #{method}, args: #{args.inspect}, target_object_id: #{@target.object_id}" }) if @target.respond_to?(method) if NON_DESTRUCTIVE_METHODS.include?(method) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL non-destructive method: #{method}" }) target_dup = @target.dup unwrapped_args = args.map { |arg| arg.is_a?(SettingsProxy) ? @settings_module.unwrap_proxies(arg) : arg } # Corrected result = target_dup.send(method, *unwrapped_args, &block) # Minimal change: pass method name so we can tag views as detached and keep path return handle_non_destructive_result(method, result) else @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL destructive method: #{method}" }) # NEW (5.12.7+): auto-reattach derived views before mutating # ensure destructive methods (.push) do not target a proxy non-destructive method (.sort) if detached? unless @settings_module._reattach_live!(self) @settings_module._log(Settings::LOG_LEVEL_ERROR, LOG_PREFIX, -> { "CALL reattach failed; aborting destructive op #{method} on detached view" }) return self end end unwrapped_args = args.map { |arg| arg.is_a?(SettingsProxy) ? @settings_module.unwrap_proxies(arg) : arg } # Corrected @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL target_before_op: #{@target.inspect}" }) result = @target.send(method, *unwrapped_args, &block) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL target_after_op: #{@target.inspect}" }) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "CALL calling save_proxy_changes on settings module" }) @settings_module.save_proxy_changes(self) return handle_method_result(result) end else super end end |
Instance Attribute Details
#path ⇒ Object (readonly)
Returns the value of attribute path.
34 35 36 |
# File 'documented/common/settings/settings_proxy.rb', line 34 def path @path end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
34 35 36 |
# File 'documented/common/settings/settings_proxy.rb', line 34 def scope @scope end |
#target ⇒ Object (readonly)
Returns the value of attribute target.
34 35 36 |
# File 'documented/common/settings/settings_proxy.rb', line 34 def target @target end |
Instance Method Details
#[](key) ⇒ Object
Retrieves a value from the target using the specified key.
288 289 290 291 292 293 294 295 296 297 298 |
# File 'documented/common/settings/settings_proxy.rb', line 288 def [](key) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "GET scope: #{@scope.inspect}, path: #{@path.inspect}, key: #{key.inspect}, target_object_id: #{@target.object_id}" }) value = @target[key] if @settings_module.container?(value) new_path = @path.dup new_path << key SettingsProxy.new(@settings_module, @scope, new_path, value) else value end end |
#[]=(key, value) ⇒ Object
Sets a value in the target using the specified key.
304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'documented/common/settings/settings_proxy.rb', line 304 def []=(key, value) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "SET scope: #{@scope.inspect}, path: #{@path.inspect}, key: #{key.inspect}, value: #{value.inspect}, target_object_id: #{@target.object_id}" }) actual_value = value.is_a?(SettingsProxy) ? @settings_module.unwrap_proxies(value) : value # Corrected to use @settings_module @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "SET target_before_set: #{@target.inspect}" }) @target[key] = actual_value @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "SET target_after_set: #{@target.inspect}" }) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "SET calling save_proxy_changes on settings module" }) @settings_module.save_proxy_changes(self) # rubocop:disable Lint/Void # This is Ruby expected behavior to return the value. value # rubocop:enable Lint/Void end |
#binary_op(operator, other) ⇒ Object
Performs a binary operation with the target and another value.
57 58 59 60 |
# File 'documented/common/settings/settings_proxy.rb', line 57 def binary_op(operator, other) other_value = other.is_a?(SettingsProxy) ? other.target : other @target.send(operator, other_value) end |
#detached? ⇒ Boolean
Minimal change: expose detached? status Checks if the proxy is in a detached state.
43 44 45 |
# File 'documented/common/settings/settings_proxy.rb', line 43 def detached? !!@detached end |
#each {|Object| ... } ⇒ self
Minimal change: items yielded from #each are “views” over the container. Mark them detached so mutations during a derived iteration won’t clobber root. Iterates over the target, yielding each item. Items yielded from #each are “views” over the container.
248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'documented/common/settings/settings_proxy.rb', line 248 def each(&_block) return enum_for(:each) unless block_given? if @target.respond_to?(:each) @target.each do |item| if @settings_module.container?(item) yield SettingsProxy.new(@settings_module, @scope, [], item, detached: true) else yield item end end end self end |
#handle_method_result(result) ⇒ Object
370 371 372 373 374 375 376 377 378 379 380 |
# File 'documented/common/settings/settings_proxy.rb', line 370 def handle_method_result(result) if result.equal?(@target) self # Return self if the method modified the target in-place and returned it elsif @settings_module.container?(result) # If a new container is returned (e.g. some destructive methods might return a new object) # Wrap it in a new proxy, maintaining the current path and scope. SettingsProxy.new(@settings_module, @scope, @path, result) else result end end |
#handle_non_destructive_result(method, result) ⇒ Object
Minimal change: keep path (not []), and tag view proxies as detached.
359 360 361 362 363 364 365 366 367 368 |
# File 'documented/common/settings/settings_proxy.rb', line 359 def handle_non_destructive_result(method, result) @settings_module.reset_path_and_return( if @settings_module.container?(result) is_view = NON_DESTRUCTIVE_CONTAINER_VIEWS.include?(method) SettingsProxy.new(@settings_module, @scope, @path.dup, result, detached: is_view) else result end ) end |
#hash ⇒ Integer
Returns the hash of the target object.
70 71 72 |
# File 'documented/common/settings/settings_proxy.rb', line 70 def hash @target.hash end |
#inspect ⇒ String
Updated inspect method to show the target’s inspect string Returns a string representation of the proxy’s target.
211 212 213 |
# File 'documented/common/settings/settings_proxy.rb', line 211 def inspect @target.inspect end |
#instance_of?(klass) ⇒ Boolean
234 235 236 |
# File 'documented/common/settings/settings_proxy.rb', line 234 def instance_of?(klass) @target.instance_of?(klass) end |
#is_a?(klass) ⇒ Boolean
226 227 228 |
# File 'documented/common/settings/settings_proxy.rb', line 226 def is_a?(klass) @target.is_a?(klass) end |
#kind_of?(klass) ⇒ Boolean
230 231 232 |
# File 'documented/common/settings/settings_proxy.rb', line 230 def kind_of?(klass) @target.kind_of?(klass) end |
#nil? ⇒ Boolean
Checks if the target is nil.
49 50 51 |
# File 'documented/common/settings/settings_proxy.rb', line 49 def nil? @target.nil? end |
#pretty_print(pp) ⇒ Object
222 223 224 |
# File 'documented/common/settings/settings_proxy.rb', line 222 def pretty_print(pp) pp.pp(@target) end |
#proxy_details ⇒ String
New method to show the proxy’s internal details Returns a string with the proxy’s internal details.
218 219 220 |
# File 'documented/common/settings/settings_proxy.rb', line 218 def proxy_details "<SettingsProxy scope=#{@scope.inspect} path=#{@path.inspect} target_class=#{@target.class} target_object_id=#{@target.object_id} detached=#{@detached}>" end |
#respond_to?(method, include_private = false) ⇒ Boolean
238 239 240 |
# File 'documented/common/settings/settings_proxy.rb', line 238 def respond_to?(method, include_private = false) super || @target.respond_to?(method, include_private) end |
#respond_to_missing?(method, include_private = false) ⇒ Boolean
Checks if the target responds to a method, including private methods.
386 387 388 |
# File 'documented/common/settings/settings_proxy.rb', line 386 def respond_to_missing?(method, include_private = false) @target.respond_to?(method, include_private) || super end |
#to_a ⇒ Object
Collection conversions
152 153 154 |
# File 'documented/common/settings/settings_proxy.rb', line 152 def to_a delegate_conversion(:to_a, default: []) end |
#to_ary ⇒ Object
156 157 158 |
# File 'documented/common/settings/settings_proxy.rb', line 156 def to_ary delegate_conversion(:to_ary, strict: true) end |
#to_c ⇒ Object
147 148 149 |
# File 'documented/common/settings/settings_proxy.rb', line 147 def to_c delegate_conversion(:to_c, default: Complex(0, 0)) end |
#to_enum(*args, &block) ⇒ Object
Special case for to_enum
198 199 200 201 202 203 204 205 206 |
# File 'documented/common/settings/settings_proxy.rb', line 198 def to_enum(*args, &block) if @target.respond_to?(:to_enum) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "to_enum: delegating" }) @target.to_enum(*args, &block) else @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "to_enum: using default enum_for" }) self.enum_for(*args, &block) end end |
#to_f ⇒ Object
139 140 141 |
# File 'documented/common/settings/settings_proxy.rb', line 139 def to_f delegate_conversion(:to_f, default: 0.0) end |
#to_h ⇒ Object
160 161 162 |
# File 'documented/common/settings/settings_proxy.rb', line 160 def to_h delegate_conversion(:to_h, default: {}) end |
#to_hash ⇒ Object
164 165 166 |
# File 'documented/common/settings/settings_proxy.rb', line 164 def to_hash delegate_conversion(:to_hash, strict: true) end |
#to_i ⇒ Integer
Returns 0 if the target does not respond to to_i.
Numeric conversions Converts the target to an integer.
131 132 133 |
# File 'documented/common/settings/settings_proxy.rb', line 131 def to_i delegate_conversion(:to_i, default: 0) end |
#to_int ⇒ Object
135 136 137 |
# File 'documented/common/settings/settings_proxy.rb', line 135 def to_int delegate_conversion(:to_int, strict: true) end |
#to_io ⇒ Object
189 190 191 |
# File 'documented/common/settings/settings_proxy.rb', line 189 def to_io delegate_conversion(:to_io, strict: true) end |
#to_json(*args) ⇒ String
added 20250620 for JSON.pretty_generate Converts the target to JSON format.
175 176 177 178 179 180 181 182 |
# File 'documented/common/settings/settings_proxy.rb', line 175 def to_json(*args) if @target.respond_to?(:to_json) @settings_module._log(Settings::LOG_LEVEL_DEBUG, LOG_PREFIX, -> { "to_json: delegating with args" }) @target.to_json(*args) else raise NoMethodError, "undefined method :to_json for #{@target.inspect}:#{@target.class}" end end |
#to_path ⇒ Object
193 194 195 |
# File 'documented/common/settings/settings_proxy.rb', line 193 def to_path delegate_conversion(:to_path, strict: true) end |
#to_proc ⇒ Object
Other common conversions
185 186 187 |
# File 'documented/common/settings/settings_proxy.rb', line 185 def to_proc delegate_conversion(:to_proc, strict: true) end |
#to_r ⇒ Object
143 144 145 |
# File 'documented/common/settings/settings_proxy.rb', line 143 def to_r delegate_conversion(:to_r, strict: true) end |
#to_s ⇒ String
String conversions Converts the target to a string.
112 113 114 |
# File 'documented/common/settings/settings_proxy.rb', line 112 def to_s delegate_conversion(:to_s, default: '') end |
#to_str ⇒ String
Converts the target to a string, strict version.
119 120 121 |
# File 'documented/common/settings/settings_proxy.rb', line 119 def to_str delegate_conversion(:to_str, strict: true) end |
#to_sym ⇒ Object
123 124 125 |
# File 'documented/common/settings/settings_proxy.rb', line 123 def to_sym delegate_conversion(:to_sym, strict: true) end |