Class: Lich::Common::SettingsProxy

Inherits:
Object
  • Object
show all
Defined in:
lib/common/settings/settings_proxy.rb

Overview

This module provides a proxy class for settings objects, allowing them to be accessed in a Ruby-compatible way. It handles both scalar and container types, and provides methods for comparison, conversion, and enumeration.

The proxy is designed to work with settings objects that are either Hashes or Arrays. It allows for nested access to settings, while also providing a way to save changes made to the settings.

The proxy also provides a way to handle non-destructive methods, which return a new object instead of modifying the original. This is done by creating a duplicate of the target object before calling the method, and then returning a new proxy for the result if it is a container type.

The proxy also handles method delegation, allowing methods to be called directly on the target object. It uses method_missing to catch calls to methods that are not defined on the proxy itself, and delegates them to the target object.

The proxy also provides a way to handle results of non-destructive methods, which return a new object instead of modifying the original. This is done by creating a duplicate of the target object before calling the method, and then returning a new proxy for the result if it is a container type.

Constant Summary collapse

NON_DESTRUCTIVE_METHODS =

Non-destructive enumerable methods that should not save changes.

[
  :select, :map, :filter, :reject, :collect, :find, :detect,
  :find_all, :grep, :grep_v, :group_by, :partition, :min, :max,
  :minmax, :min_by, :max_by, :minmax_by, :sort, :sort_by,
  :flat_map, :collect_concat, :reduce, :inject, :sum, :count,
  :cycle, :drop, :drop_while, :take, :take_while, :first, :all?,
  :any?, :none?, :one?, :find_index, :values_at, :zip, :reverse,
  :entries, :to_a, :to_h, :include?, :member?, :each_with_index,
  :each_with_object, :each_entry, :each_slice, :each_cons, :chunk,
  :slice_before, :slice_after, :slice_when, :chunk_while, :lazy
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings, path, target) ⇒ SettingsProxy

Initializes a new SettingsProxy instance.

Parameters:

  • settings (Object)

    The settings object being proxied.

  • path (Array)

    The path to the current settings.

  • target (Object)

    The target object being proxied.



31
32
33
34
35
# File 'lib/common/settings/settings_proxy.rb', line 31

def initialize(settings, path, target)
  @settings = settings
  @path = path.dup
  @target = target
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Handles method calls that are not defined on the proxy.

Parameters:

  • method (Symbol)

    The method name to call.

  • args (Array)

    The arguments to pass to the method.

  • block (Proc)

    The block to pass to the method.

Returns:

  • (Object)

    The result of the method call.

Raises:

  • (NoMethodError)

    If the method is not defined on the target.



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/common/settings/settings_proxy.rb', line 265

def method_missing(method, *args, &block)
  if @target.respond_to?(method)
    # For non-destructive methods, operate on a duplicate to avoid modifying original
    if NON_DESTRUCTIVE_METHODS.include?(method)
      # Create a duplicate of the target for non-destructive operations
      target_dup = @target.dup
      result = target_dup.send(method, *args, &block)

      # Return the result without saving changes
      return handle_non_destructive_result(result)
    else
      # For destructive methods, operate on the original and save changes
      result = @target.send(method, *args, &block)
      @settings.save_proxy_changes(self)
      return handle_method_result(result)
    end
  else
    super
  end
end

Instance Attribute Details

#pathObject (readonly)

Allow access to the target for debugging

Returns:

  • (Object)

    The target object.



40
41
42
# File 'lib/common/settings/settings_proxy.rb', line 40

def path
  @path
end

#targetObject (readonly)

Allow access to the target for debugging

Returns:

  • (Object)

    The target object.



40
41
42
# File 'lib/common/settings/settings_proxy.rb', line 40

def target
  @target
end

Instance Method Details

#[](key) ⇒ Object

Accesses a value in the target by key.

Parameters:

  • key (Object)

    The key to access the value.

Returns:

  • (Object)

    The value associated with the key, or a new SettingsProxy if the value is a container.



229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/common/settings/settings_proxy.rb', line 229

def [](key)
  value = @target[key]

  if Settings.container?(value)
    # For container types, return a new proxy with updated path
    new_path = @path.dup
    new_path << key
    SettingsProxy.new(@settings, new_path, value)
  else
    # For scalar values, return the value directly
    value
  end
end

#[]=(key, value) ⇒ Object

Sets a value in the target by key.

Parameters:

  • key (Object)

    The key to set the value.

  • value (Object)

    The value to set.

Returns:

  • (Object)

    The value that was set.



248
249
250
251
252
# File 'lib/common/settings/settings_proxy.rb', line 248

def []=(key, value)
  @target[key] = value
  @settings.save_proxy_changes(self)
  # value
end

#binary_op(operator, other) ⇒ Object

Helper method for binary operators to reduce repetition.

Parameters:

  • operator (Symbol)

    The binary operator to apply.

  • other (Object)

    The other operand.

Returns:

  • (Object)

    The result of the binary operation.



58
59
60
61
# File 'lib/common/settings/settings_proxy.rb', line 58

def binary_op(operator, other)
  other_value = other.is_a?(SettingsProxy) ? other.target : other
  @target.send(operator, other_value)
end

#each {|Object| ... } ⇒ self

Iterates over each item in the target.

Yields:

  • (Object)

    The block to execute for each item.

Returns:

  • (self)

    The SettingsProxy instance.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/common/settings/settings_proxy.rb', line 192

def each(&_block)
  return enum_for(:each) unless block_given?

  if @target.respond_to?(:each)
    @target.each do |item|
      if Settings.container?(item)
        yield SettingsProxy.new(@settings, [], item)
      else
        yield item
      end
    end
  end

  self
end

#handle_method_result(result) ⇒ Object

Helper method to handle results of destructive methods.

Parameters:

  • result (Object)

    The result of the destructive method.

Returns:

  • (Object)

    The wrapped result in a new SettingsProxy if it’s a container, or the result itself.



307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/common/settings/settings_proxy.rb', line 307

def handle_method_result(result)
  if result.equal?(@target)
    # If result is the original target, return self
    self
  elsif Settings.container?(result)
    # For container results, wrap in a new proxy with current path
    SettingsProxy.new(@settings, @path, result)
  else
    # For scalar results, return directly
    result
  end
end

#handle_non_destructive_result(result) ⇒ Object

Helper method to handle results of non-destructive methods.

Parameters:

  • result (Object)

    The result of the non-destructive method.

Returns:

  • (Object)

    The wrapped result in a new SettingsProxy if it’s a container, or the result itself.



290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/common/settings/settings_proxy.rb', line 290

def handle_non_destructive_result(result)
  # No need to capture path since we're using empty path
  @settings.reset_path_and_return(
    if Settings.container?(result)
      # For container results, wrap in a new proxy with empty path
      SettingsProxy.new(@settings, [], result)
    else
      # For scalar results, return directly
      result
    end
  )
end

#hashInteger

Returns the hash code of the target.

Returns:

  • (Integer)

    The hash code of the target.



76
77
78
# File 'lib/common/settings/settings_proxy.rb', line 76

def hash
  @target.hash
end

#inspectString

Returns a string representation of the target for debugging.

Returns:

  • (String)

    The inspected string representation of the target.



90
91
92
# File 'lib/common/settings/settings_proxy.rb', line 90

def inspect
  @target.inspect
end

#instance_of?(klass) ⇒ Boolean

Checks if the target is an instance of the given class.

Parameters:

  • klass (Class)

    The class to check against.

Returns:

  • (Boolean)

    True if the target is an instance of the class, false otherwise.



126
127
128
# File 'lib/common/settings/settings_proxy.rb', line 126

def instance_of?(klass)
  @target.instance_of?(klass)
end

#is_a?(klass) ⇒ Boolean

Checks if the target is an instance of the given class.

Parameters:

  • klass (Class)

    The class to check against.

Returns:

  • (Boolean)

    True if the target is an instance of the class, false otherwise.



110
111
112
# File 'lib/common/settings/settings_proxy.rb', line 110

def is_a?(klass)
  @target.is_a?(klass)
end

#kind_of?(klass) ⇒ Boolean

Checks if the target is a kind of the given class.

Parameters:

  • klass (Class)

    The class to check against.

Returns:

  • (Boolean)

    True if the target is a kind of the class, false otherwise.



118
119
120
# File 'lib/common/settings/settings_proxy.rb', line 118

def kind_of?(klass)
  @target.kind_of?(klass)
end

#methodObject

Define conversion methods using metaprogramming to reduce repetition.

Returns:

  • (Object)

    The result of the conversion method.



178
179
180
181
182
# File 'lib/common/settings/settings_proxy.rb', line 178

[:to_int, :to_i, :to_str, :to_sym, :to_proc].each do |method|
  define_method(method) do
    @target.send(method) if @target.respond_to?(method)
  end
end

#nil?Boolean

Checks if the target is nil.

Returns:

  • (Boolean)

    True if the target is nil, false otherwise.



49
50
51
# File 'lib/common/settings/settings_proxy.rb', line 49

def nil?
  @target.nil?
end

#opBoolean

Define comparison operators using metaprogramming to reduce repetition. NB: not all operators apply to all objects (e.g. <, >, <=, >= on Arrays).

Returns:

  • (Boolean)

    The result of the comparison.



67
68
69
70
71
# File 'lib/common/settings/settings_proxy.rb', line 67

[:==, :!=, :eql?, :equal?, :<=>, :<, :<=, :>, :>=, :|, :&].each do |op|
  define_method(op) do |other|
    binary_op(op, other)
  end
end

#pretty_print(pp) ⇒ void

This method returns an undefined value.

Pretty prints the target.

Parameters:

  • pp (Object)

    The pretty print object.



98
99
100
# File 'lib/common/settings/settings_proxy.rb', line 98

def pretty_print(pp)
  pp.pp(@target)
end

#respond_to?(method, include_private = false) ⇒ Boolean

Checks if the target responds to the given method.

Parameters:

  • method (Symbol)

    The method name to check.

  • include_private (Boolean) (defaults to: false)

    Whether to include private methods in the check.

Returns:

  • (Boolean)

    True if the target responds to the method, false otherwise.



135
136
137
# File 'lib/common/settings/settings_proxy.rb', line 135

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 the given method.

Parameters:

  • method (Symbol)

    The method name to check.

  • include_private (Boolean) (defaults to: false)

    Whether to include private methods in the check.

Returns:

  • (Boolean)

    True if the target responds to the method, false otherwise.



325
326
327
# File 'lib/common/settings/settings_proxy.rb', line 325

def respond_to_missing?(method, include_private = false)
  @target.respond_to?(method, include_private) || super
end

#to_aArray?

Converts the target to an array (alias for to_ary).

Returns:

  • (Array, nil)

    A duplicate of the target if it is an Array, nil otherwise.



171
172
173
# File 'lib/common/settings/settings_proxy.rb', line 171

def to_a
  to_ary
end

#to_aryArray?

Converts the target to an array.

Returns:

  • (Array, nil)

    A duplicate of the target if it is an Array, nil otherwise.



162
163
164
165
166
# File 'lib/common/settings/settings_proxy.rb', line 162

def to_ary
  return nil unless @target.is_a?(Array)

  @target.dup
end

#to_hHash?

Converts the target to a hash (alias for to_hash).

Returns:

  • (Hash, nil)

    A duplicate of the target if it is a Hash, nil otherwise.



155
156
157
# File 'lib/common/settings/settings_proxy.rb', line 155

def to_h
  to_hash
end

#to_hashHash?

Converts the target to a hash.

Returns:

  • (Hash, nil)

    A duplicate of the target if it is a Hash, nil otherwise.



146
147
148
149
150
# File 'lib/common/settings/settings_proxy.rb', line 146

def to_hash
  return nil unless @target.is_a?(Hash)

  @target.dup
end

#to_sString

Returns a string representation of the target.

Returns:

  • (String)

    The string representation of the target.



83
84
85
# File 'lib/common/settings/settings_proxy.rb', line 83

def to_s
  @target.to_s
end