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 dynamic delegation of method calls to a target object, while maintaining a context of scope and path for settings management.
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.
-
#script_name ⇒ Object
readonly
Returns the value of attribute script_name.
-
#target ⇒ Object
readonly
Returns the value of attribute target.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #[]=(key, value) ⇒ Object
-
#binary_op(operator, other) ⇒ Object
Performs a binary operation with the target object.
-
#detached? ⇒ Boolean
Checks if the proxy is detached from its target.
- #each(&_block) ⇒ Object
-
#handle_method_result(result) ⇒ Object
Handles the result of method calls that may modify the target.
-
#handle_non_destructive_result(method, result) ⇒ Object
Handles the result of non-destructive method calls.
-
#hash ⇒ Integer
Returns the hash value of the target object.
-
#initialize(settings_module, scope, path, target, detached: false, script_name: nil) ⇒ SettingsProxy
constructor
Initializes a new SettingsProxy instance.
- #inspect ⇒ Object
- #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
- #pretty_print(pp) ⇒ Object
- #proxy_details ⇒ Object
- #respond_to?(method, include_private = false) ⇒ Boolean
- #respond_to_missing?(method, include_private = false) ⇒ Boolean
- #to_a ⇒ Object
- #to_ary ⇒ Object
- #to_c ⇒ Object
- #to_enum(*args, &block) ⇒ Object
- #to_f ⇒ Object
- #to_h ⇒ Object
- #to_hash ⇒ Object
- #to_i ⇒ Object
- #to_int ⇒ Object
- #to_io ⇒ Object
-
#to_json(*args) ⇒ String
Converts the target object to JSON format.
- #to_path ⇒ Object
- #to_proc ⇒ Object
- #to_r ⇒ Object
- #to_s ⇒ Object
- #to_str ⇒ Object
- #to_sym ⇒ Object
Constructor Details
#initialize(settings_module, scope, path, target, detached: false, script_name: nil) ⇒ SettingsProxy
Initializes a new SettingsProxy instance.
21 22 23 24 25 26 27 28 29 |
# File 'documented/common/settings/settings_proxy.rb', line 21 def initialize(settings_module, scope, path, target, detached: false, script_name: nil) @settings_module = settings_module # This should be the Settings module itself @scope = scope @path = path.dup @target = target @detached = detached @script_name = script_name @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}, script_name: #{@script_name.inspect}" }) 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.
This method delegates the call to the target object if it responds to the method.
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 |
# File 'documented/common/settings/settings_proxy.rb', line 286 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.
31 32 33 |
# File 'documented/common/settings/settings_proxy.rb', line 31 def path @path end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
31 32 33 |
# File 'documented/common/settings/settings_proxy.rb', line 31 def scope @scope end |
#script_name ⇒ Object (readonly)
Returns the value of attribute script_name.
31 32 33 |
# File 'documented/common/settings/settings_proxy.rb', line 31 def script_name @script_name end |
#target ⇒ Object (readonly)
Returns the value of attribute target.
31 32 33 |
# File 'documented/common/settings/settings_proxy.rb', line 31 def target @target end |
Instance Method Details
#[](key) ⇒ Object
252 253 254 255 256 257 258 259 260 261 262 |
# File 'documented/common/settings/settings_proxy.rb', line 252 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, script_name: @script_name) else value end end |
#[]=(key, value) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'documented/common/settings/settings_proxy.rb', line 264 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 object.
49 50 51 52 |
# File 'documented/common/settings/settings_proxy.rb', line 49 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 its target.
36 37 38 |
# File 'documented/common/settings/settings_proxy.rb', line 36 def detached? !!@detached end |
#each(&_block) ⇒ Object
215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'documented/common/settings/settings_proxy.rb', line 215 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, script_name: @script_name) else yield item end end end self end |
#handle_method_result(result) ⇒ Object
Handles the result of method calls that may modify the target.
360 361 362 363 364 365 366 367 368 369 370 |
# File 'documented/common/settings/settings_proxy.rb', line 360 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, script_name: @script_name) else result end end |
#handle_non_destructive_result(method, result) ⇒ Object
Handles the result of non-destructive method calls.
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 |
# File 'documented/common/settings/settings_proxy.rb', line 326 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, script_name: @script_name) 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, script_name: @script_name) 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, script_name: @script_name) end else # Non-container results (e.g., Hash#keys) stay as plain values result end ) end |
#hash ⇒ Integer
Returns the hash value of the target object.
63 64 65 |
# File 'documented/common/settings/settings_proxy.rb', line 63 def hash @target.hash end |
#inspect ⇒ Object
187 188 189 |
# File 'documented/common/settings/settings_proxy.rb', line 187 def inspect @target.inspect end |
#instance_of?(klass) ⇒ Boolean
207 208 209 |
# File 'documented/common/settings/settings_proxy.rb', line 207 def instance_of?(klass) @target.instance_of?(klass) end |
#is_a?(klass) ⇒ Boolean
199 200 201 |
# File 'documented/common/settings/settings_proxy.rb', line 199 def is_a?(klass) @target.is_a?(klass) end |
#kind_of?(klass) ⇒ Boolean
203 204 205 |
# File 'documented/common/settings/settings_proxy.rb', line 203 def kind_of?(klass) @target.kind_of?(klass) end |
#nil? ⇒ Boolean
40 41 42 |
# File 'documented/common/settings/settings_proxy.rb', line 40 def nil? @target.nil? end |
#pretty_print(pp) ⇒ Object
195 196 197 |
# File 'documented/common/settings/settings_proxy.rb', line 195 def pretty_print(pp) pp.pp(@target) end |
#proxy_details ⇒ Object
191 192 193 |
# File 'documented/common/settings/settings_proxy.rb', line 191 def proxy_details "<SettingsProxy scope=#{@scope.inspect} path=#{@path.inspect} target_class=#{@target.class} target_object_id=#{@target.object_id} detached=#{@detached} script_name=#{@script_name.inspect}>" end |
#respond_to?(method, include_private = false) ⇒ Boolean
211 212 213 |
# File 'documented/common/settings/settings_proxy.rb', line 211 def respond_to?(method, include_private = false) super || @target.respond_to?(method, include_private) end |
#respond_to_missing?(method, include_private = false) ⇒ Boolean
372 373 374 |
# File 'documented/common/settings/settings_proxy.rb', line 372 def respond_to_missing?(method, include_private = false) @target.respond_to?(method, include_private) || super end |
#to_a ⇒ Object
135 136 137 |
# File 'documented/common/settings/settings_proxy.rb', line 135 def to_a delegate_conversion(:to_a, default: []) end |
#to_ary ⇒ Object
139 140 141 |
# File 'documented/common/settings/settings_proxy.rb', line 139 def to_ary delegate_conversion(:to_ary, strict: true) end |
#to_c ⇒ Object
131 132 133 |
# File 'documented/common/settings/settings_proxy.rb', line 131 def to_c delegate_conversion(:to_c, default: Complex(0, 0)) end |
#to_enum(*args, &block) ⇒ Object
177 178 179 180 181 182 183 184 185 |
# File 'documented/common/settings/settings_proxy.rb', line 177 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
123 124 125 |
# File 'documented/common/settings/settings_proxy.rb', line 123 def to_f delegate_conversion(:to_f, default: 0.0) end |
#to_h ⇒ Object
143 144 145 |
# File 'documented/common/settings/settings_proxy.rb', line 143 def to_h delegate_conversion(:to_h, default: {}) end |
#to_hash ⇒ Object
147 148 149 |
# File 'documented/common/settings/settings_proxy.rb', line 147 def to_hash delegate_conversion(:to_hash, strict: true) end |
#to_i ⇒ Object
115 116 117 |
# File 'documented/common/settings/settings_proxy.rb', line 115 def to_i delegate_conversion(:to_i, default: 0) end |
#to_int ⇒ Object
119 120 121 |
# File 'documented/common/settings/settings_proxy.rb', line 119 def to_int delegate_conversion(:to_int, strict: true) end |
#to_io ⇒ Object
169 170 171 |
# File 'documented/common/settings/settings_proxy.rb', line 169 def to_io delegate_conversion(:to_io, strict: true) end |
#to_json(*args) ⇒ String
Converts the target object to JSON format.
156 157 158 159 160 161 162 163 |
# File 'documented/common/settings/settings_proxy.rb', line 156 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
173 174 175 |
# File 'documented/common/settings/settings_proxy.rb', line 173 def to_path delegate_conversion(:to_path, strict: true) end |
#to_proc ⇒ Object
165 166 167 |
# File 'documented/common/settings/settings_proxy.rb', line 165 def to_proc delegate_conversion(:to_proc, strict: true) end |
#to_r ⇒ Object
127 128 129 |
# File 'documented/common/settings/settings_proxy.rb', line 127 def to_r delegate_conversion(:to_r, strict: true) end |
#to_s ⇒ Object
103 104 105 |
# File 'documented/common/settings/settings_proxy.rb', line 103 def to_s delegate_conversion(:to_s, default: '') end |
#to_str ⇒ Object
107 108 109 |
# File 'documented/common/settings/settings_proxy.rb', line 107 def to_str delegate_conversion(:to_str, strict: true) end |
#to_sym ⇒ Object
111 112 113 |
# File 'documented/common/settings/settings_proxy.rb', line 111 def to_sym delegate_conversion(:to_sym, strict: true) end |