class CowProxy::Base

Base class to create CowProxy classes

Also, it's used as default CowProxy class for non-registered classes with copy-on-write disabled, so returned values are wrapped but methods trying to change object still raise exception.

Attributes

wrapped_class[RW]

Class which will be wrapped with this CowProxy class

Public Class Methods

inherited(subclass) click to toggle source

Setup wrapped_class and register itself into CowProxy with {CowProxy.register_proxy CowProxy.register_proxy}

# File lib/cow_proxy/base.rb, line 14
def inherited(subclass)
  subclass.wrapped_class = wrapped_class
  CowProxy.register_proxy wrapped_class, subclass if wrapped_class
end
new(obj, parent = nil, parent_var = nil) click to toggle source

Creates a CowProxy object wrapping obj

@param obj An object to wrap with CowProxy class @param parent CowProxy object wrapping obj @param parent_var instance variable name in parent

which keeps this CowProxy object
# File lib/cow_proxy/base.rb, line 50
def initialize(obj, parent = nil, parent_var = nil)
  @delegate_dc_obj = obj
  @parent_proxy = parent
  @parent_var = parent_var
  @dc_obj_duplicated = false
end

Protected Class Methods

wrapping_block(method, cow_enabled) click to toggle source

Return block with proxy implementation.

Block calls a method in wrapped object

@param [Symbol] method Method name to call in wrapped object @param [Boolean] cow_enabled True if copy-on-write is enabled

for proxy class, it will _copy_on_write when method will
modify wrapped object.

@return [Proc] Block with proxy implementation.

# File lib/cow_proxy/base.rb, line 30
def wrapping_block(method, cow_enabled)
  lambda do |*args, &block|
    if method.to_s =~ /^\w+$/
      inst_var = "@#{method}"
      return _instance_variable_get(inst_var) if _instance_variable_defined?(inst_var)
    elsif method.to_s =~ /^(\w+)=$/ && _instance_variable_defined?("@#{Regexp.last_match(1)}")
      CowProxy.debug { "remove #{Regexp.last_match(1)}" }
      _remove_instance_variable "@#{Regexp.last_match(1)}"
    end
    __wrapped_method__(inst_var, cow_enabled, method, *args, &block)
  end
end

Protected Instance Methods

__copy_on_write__(parent = true) click to toggle source

Replace wrapped object with a copy, so object can be modified.

@param [Boolean] parent Replace proxy object in parent with

duplicated wrapped object, if this proxy was created from
another CowProxy.

@return duplicated wrapped object

# File lib/cow_proxy/base.rb, line 66
def __copy_on_write__(parent = true)
  CowProxy.debug { "copy on write on #{__getobj__.class.name}" }
  return @delegate_dc_obj if @dc_obj_duplicated
  @delegate_dc_obj = @delegate_dc_obj.dup
  @dc_obj_duplicated = true
  __copy_parent__ if parent && @parent_proxy
  @delegate_dc_obj
end

Private Instance Methods

__copy_parent__() click to toggle source
# File lib/cow_proxy/base.rb, line 81
def __copy_parent__
  @parent_proxy.send :__copy_on_write__, false
  return unless @parent_var
  parent_dc = @parent_proxy._instance_variable_get(:@delegate_dc_obj)
  method = @parent_var[1..-1] + '='
  parent_dc.send(method, @delegate_dc_obj)
end
__getobj__() click to toggle source
# File lib/cow_proxy/base.rb, line 77
def __getobj__
  @delegate_dc_obj
end
__wrap__(value, inst_var = nil) click to toggle source
# File lib/cow_proxy/base.rb, line 89
def __wrap__(value, inst_var = nil)
  return unless value.frozen?
  CowProxy.debug { "wrap #{value.class.name} with parent #{__getobj__.class.name}" }
  wrap_klass = CowProxy.wrapper_class(value)
  wrap_value = wrap_klass&.new(value, self, inst_var)
  _instance_variable_set(inst_var, wrap_value) if inst_var && wrap_value
  wrap_value
end
__wrapped_method__(inst_var, cow, method, *args, &block) click to toggle source
# File lib/cow_proxy/base.rb, line 108
def __wrapped_method__(inst_var, cow, method, *args, &block)
  __wrapped_value__(inst_var, method, *args, &block)
rescue StandardError => e
  CowProxy.debug do
    "error #{e.message} on #{__getobj__.class.name} (#{__getobj__.object_id}) #{method} "\
    "#{args.inspect unless args.empty?} with#{'out' unless cow} cow"
  end
  raise unless cow && e.message =~ /^can't modify frozen/
  CowProxy.debug { "copy on write to run #{method}" }
  __copy_on_write__
  CowProxy.debug { "new target #{__getobj__.class.name} (#{__getobj__.object_id})" }
  __wrapped_value__(inst_var, method, *args, &block)
end
__wrapped_value__(inst_var, method, *args, &block) click to toggle source
# File lib/cow_proxy/base.rb, line 98
def __wrapped_value__(inst_var, method, *args, &block)
  CowProxy.debug do
    "run on #{__getobj__.class.name} (#{__getobj__.object_id}) "\
    "#{method} #{args.inspect unless args.empty?}"
  end
  value = __getobj__.__send__(method, *args, &block)
  wrap_value = __wrap__(value, inst_var) if inst_var && args.empty? && !block
  wrap_value || value
end