Class: Lich::Common::SettingsProxy
- Inherits:
-
Object
- Object
- Lich::Common::SettingsProxy
- Defined in:
- documented/common/settings/settings_proxy.rb
Overview
A proxy class for managing settings with a target object. This class allows for delegation of method calls to the target object, while providing additional functionality such as logging and path management.
Constant Summary collapse
- LOG_PREFIX =
"[SettingsProxy]".freeze
- NON_DESTRUCTIVE_METHODS =
A list of non-destructive methods that can be called on the target.
[ :+, :-, :&, :|, :*, :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” A 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
Provides read access to the target, path, and scope attributes.
-
#scope ⇒ Object
readonly
Provides read access to the target, path, and scope attributes.
-
#target ⇒ Object
readonly
Provides read access to the target, path, and scope attributes.
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
Checks if the proxy is detached from the target.
- #each(&_block) ⇒ Object
- #handle_method_result(result) ⇒ Object
- #handle_non_destructive_result(method, result) ⇒ Object
-
#hash ⇒ Integer
Returns the hash of the target object.
-
#initialize(settings_module, scope, path, target, detached: false) ⇒ SettingsProxy
constructor
Initializes a new SettingsProxy instance.
-
#inspect ⇒ String
Returns a string representation of the target for inspection.
- #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 in the proxy.
-
#nil? ⇒ Boolean
Checks if the target is nil.
-
#pretty_print(pp) ⇒ Object
Pretty prints the target object.
-
#proxy_details ⇒ String
Returns a string with details about the proxy.
- #respond_to?(method, include_private = false) ⇒ Boolean
-
#respond_to_missing?(method, include_private = false) ⇒ Boolean
Checks if the target responds to a method that is not explicitly defined.
-
#to_a ⇒ Array
Converts the target to an array, returning a default value if not possible.
-
#to_ary ⇒ Array
Converts the target to an array, raising an error if not possible.
-
#to_c ⇒ Complex
Converts the target to a complex number, returning a default value if not possible.
-
#to_enum(*args, &block) ⇒ Enumerator
Converts the target to an enumerator, using default if not possible.
-
#to_f ⇒ Float
Converts the target to a float, returning a default value if not possible.
-
#to_h ⇒ Hash
Converts the target to a hash, returning a default value if not possible.
-
#to_hash ⇒ Hash
Converts the target to a hash, raising an error if not possible.
-
#to_i ⇒ Integer
Converts the target to an integer, returning a default value if not possible.
-
#to_int ⇒ Integer
Converts the target to an integer, raising an error if not possible.
-
#to_io ⇒ IO
Converts the target to an IO object, raising an error if not possible.
-
#to_json(*args) ⇒ String
Converts the target to JSON format.
-
#to_path ⇒ String
Converts the target to a path, raising an error if not possible.
-
#to_proc ⇒ Proc
Converts the target to a Proc, raising an error if not possible.
-
#to_r ⇒ Rational
Converts the target to a rational number, raising an error if not possible.
-
#to_s ⇒ String
Converts the target to a string.
-
#to_str ⇒ String
Converts the target to a string, raising an error if not possible.
-
#to_sym ⇒ Symbol
Converts the target to a symbol, raising an error if not possible.
Constructor Details
#initialize(settings_module, scope, path, target, detached: false) ⇒ SettingsProxy
Initializes a new SettingsProxy instance.
18 19 20 21 22 23 24 25 |
# File 'documented/common/settings/settings_proxy.rb', line 18 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 in the proxy.
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 357 358 359 360 |
# File 'documented/common/settings/settings_proxy.rb', line 327 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)
Provides read access to the target, path, and scope attributes.
28 29 30 |
# File 'documented/common/settings/settings_proxy.rb', line 28 def path @path end |
#scope ⇒ Object (readonly)
Provides read access to the target, path, and scope attributes.
28 29 30 |
# File 'documented/common/settings/settings_proxy.rb', line 28 def scope @scope end |
#target ⇒ Object (readonly)
Provides read access to the target, path, and scope attributes.
28 29 30 |
# File 'documented/common/settings/settings_proxy.rb', line 28 def target @target end |
Instance Method Details
#[](key) ⇒ Object
Retrieves a value from the target using the specified key.
292 293 294 295 296 297 298 299 300 301 302 |
# File 'documented/common/settings/settings_proxy.rb', line 292 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.
308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'documented/common/settings/settings_proxy.rb', line 308 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.
46 47 48 49 |
# File 'documented/common/settings/settings_proxy.rb', line 46 def binary_op(operator, other) other_value = other.is_a?(SettingsProxy) ? other.target : other @target.send(operator, other_value) end |
#detached? ⇒ Boolean
Checks if the proxy is detached from the target.
32 33 34 |
# File 'documented/common/settings/settings_proxy.rb', line 32 def detached? !!@detached end |
#each(&_block) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'documented/common/settings/settings_proxy.rb', line 250 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
392 393 394 395 396 397 398 399 400 401 402 |
# File 'documented/common/settings/settings_proxy.rb', line 392 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
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'documented/common/settings/settings_proxy.rb', line 362 def handle_non_destructive_result(method, result) @settings_module.reset_path_and_return( if @settings_module.container?(result) if @target.is_a?(Array) && [:find, :detect].include?(method) # For Array#find / Array#detect, identify the element index in the # original array and create a proxy that points to that element. idx = @target.index(result) if !idx.nil? element_path = @path.dup element_path << idx SettingsProxy.new(@settings_module, @scope, element_path, result) else # Fallback: if we somehow can't locate the element, preserve # the old behavior (path == @path, no index). is_view = NON_DESTRUCTIVE_CONTAINER_VIEWS.include?(method) SettingsProxy.new(@settings_module, @scope, @path.dup, result, detached: is_view) end else # Existing behavior for all other non-destructive container methods is_view = NON_DESTRUCTIVE_CONTAINER_VIEWS.include?(method) SettingsProxy.new(@settings_module, @scope, @path.dup, result, detached: is_view) end else # Non-container results (e.g., Hash#keys) stay as plain values result end ) end |
#hash ⇒ Integer
Returns the hash of the target object.
59 60 61 |
# File 'documented/common/settings/settings_proxy.rb', line 59 def hash @target.hash end |
#inspect ⇒ String
Returns a string representation of the target for inspection.
218 219 220 |
# File 'documented/common/settings/settings_proxy.rb', line 218 def inspect @target.inspect end |
#instance_of?(klass) ⇒ Boolean
242 243 244 |
# File 'documented/common/settings/settings_proxy.rb', line 242 def instance_of?(klass) @target.instance_of?(klass) end |
#is_a?(klass) ⇒ Boolean
234 235 236 |
# File 'documented/common/settings/settings_proxy.rb', line 234 def is_a?(klass) @target.is_a?(klass) end |
#kind_of?(klass) ⇒ Boolean
238 239 240 |
# File 'documented/common/settings/settings_proxy.rb', line 238 def kind_of?(klass) @target.kind_of?(klass) end |
#nil? ⇒ Boolean
Checks if the target is nil.
38 39 40 |
# File 'documented/common/settings/settings_proxy.rb', line 38 def nil? @target.nil? end |
#pretty_print(pp) ⇒ Object
Pretty prints the target object.
230 231 232 |
# File 'documented/common/settings/settings_proxy.rb', line 230 def pretty_print(pp) pp.pp(@target) end |
#proxy_details ⇒ String
Returns a string with details about the proxy.
224 225 226 |
# File 'documented/common/settings/settings_proxy.rb', line 224 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
246 247 248 |
# File 'documented/common/settings/settings_proxy.rb', line 246 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 that is not explicitly defined.
408 409 410 |
# File 'documented/common/settings/settings_proxy.rb', line 408 def respond_to_missing?(method, include_private = false) @target.respond_to?(method, include_private) || super end |
#to_a ⇒ Array
Converts the target to an array, returning a default value if not possible.
144 145 146 |
# File 'documented/common/settings/settings_proxy.rb', line 144 def to_a delegate_conversion(:to_a, default: []) end |
#to_ary ⇒ Array
Converts the target to an array, raising an error if not possible.
151 152 153 |
# File 'documented/common/settings/settings_proxy.rb', line 151 def to_ary delegate_conversion(:to_ary, strict: true) end |
#to_c ⇒ Complex
Converts the target to a complex number, returning a default value if not possible.
138 139 140 |
# File 'documented/common/settings/settings_proxy.rb', line 138 def to_c delegate_conversion(:to_c, default: Complex(0, 0)) end |
#to_enum(*args, &block) ⇒ Enumerator
Converts the target to an enumerator, using default if not possible.
206 207 208 209 210 211 212 213 214 |
# File 'documented/common/settings/settings_proxy.rb', line 206 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 ⇒ Float
Converts the target to a float, returning a default value if not possible.
125 126 127 |
# File 'documented/common/settings/settings_proxy.rb', line 125 def to_f delegate_conversion(:to_f, default: 0.0) end |
#to_h ⇒ Hash
Converts the target to a hash, returning a default value if not possible.
157 158 159 |
# File 'documented/common/settings/settings_proxy.rb', line 157 def to_h delegate_conversion(:to_h, default: {}) end |
#to_hash ⇒ Hash
Converts the target to a hash, raising an error if not possible.
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
Converts the target to an integer, returning a default value if not possible.
112 113 114 |
# File 'documented/common/settings/settings_proxy.rb', line 112 def to_i delegate_conversion(:to_i, default: 0) end |
#to_int ⇒ Integer
Converts the target to an integer, raising an error if not possible.
119 120 121 |
# File 'documented/common/settings/settings_proxy.rb', line 119 def to_int delegate_conversion(:to_int, strict: true) end |
#to_io ⇒ IO
Converts the target to an IO object, raising an error if not possible.
191 192 193 |
# File 'documented/common/settings/settings_proxy.rb', line 191 def to_io delegate_conversion(:to_io, strict: true) end |
#to_json(*args) ⇒ String
Converts the target to JSON format.
172 173 174 175 176 177 178 179 |
# File 'documented/common/settings/settings_proxy.rb', line 172 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 ⇒ String
Converts the target to a path, raising an error if not possible.
198 199 200 |
# File 'documented/common/settings/settings_proxy.rb', line 198 def to_path delegate_conversion(:to_path, strict: true) end |
#to_proc ⇒ Proc
Converts the target to a Proc, raising an error if not possible.
184 185 186 |
# File 'documented/common/settings/settings_proxy.rb', line 184 def to_proc delegate_conversion(:to_proc, strict: true) end |
#to_r ⇒ Rational
Converts the target to a rational number, raising an error if not possible.
132 133 134 |
# File 'documented/common/settings/settings_proxy.rb', line 132 def to_r delegate_conversion(:to_r, strict: true) end |
#to_s ⇒ String
Converts the target to a string.
92 93 94 |
# File 'documented/common/settings/settings_proxy.rb', line 92 def to_s delegate_conversion(:to_s, default: '') end |
#to_str ⇒ String
Converts the target to a string, raising an error if not possible.
99 100 101 |
# File 'documented/common/settings/settings_proxy.rb', line 99 def to_str delegate_conversion(:to_str, strict: true) end |
#to_sym ⇒ Symbol
Converts the target to a symbol, raising an error if not possible.
106 107 108 |
# File 'documented/common/settings/settings_proxy.rb', line 106 def to_sym delegate_conversion(:to_sym, strict: true) end |