class SubObject

Parent class for SubString and SubObject::SubArray or similar.

Summary

This class SubObject is the parent class for SubString and alike. This class expresses Ruby sub-Object (like Sub-String), which are obtained with the +self[i, j]+ method, but taking up negligible memory space, as its instance internally holds the (likely positional, though arbitrary) information +(i, j)+ only. It provides the basic interface so the instance behaves exactly like the original class (String for SubString, for example) as duck-typing, except destructive modification, which is prohibited.

If the original object is destructively modified in a way it changes its hash value, warning is issued whenever this instance is accessed.

Important note to the developers

To inherit this class, make sure to include the lines equivalent to the following 2 lines, replacing the method name :to_str (which is for SubString class) to suit your child class.

TO_SOURCE_METHOD = :to_str
alias_method TO_SOURCE_METHOD, :to_source

The full reference is found in the top page SubObject and in Github

@author Masa Sakano (Wise Babel Ltd)

Constants

DESTRUCTIVE_METHODS

List of the names (String) of destructive methods (other than those ending with “!”).

ERR_MSGS

Warning/Error messages.

TO_SOURCE_METHOD

Symbol of the method that projects to (returns) the original-like instance; e.g., :to_str for String. The value should be overwritten in the child class of {SubObject}.

Attributes

attr[RW]

Setter/Getter of the attribute. nil in default.

pos[R]

Starting (character) position

Public Class Methods

new(source, pos, size, attr: nil) click to toggle source

Returns a new instance of SubObject equivalent to source[ pos, size ]

@param source [Object] source Object @param pos [Integer, Object] The first index for the method #[], usually the starting index position. @param size [Integer, Object] The second index for the method #[], usually the size. @param attr: [Object] user-specified arbitrary object

# File lib/sub_object.rb, line 92
def initialize(source, pos, size, attr: nil)
  @source, @pos, @isize = source, pos, size
  @attr = attr

  # Sanity check
  begin
    _ = @source[@pos, @isize]
  rescue NoMethodError
    raise TypeError, ERR_MSGS[:no_method_error]
  rescue ArgumentError
    raise TypeError, ERR_MSGS[:argument_error]
  rescue TypeError
    raise TypeError, ERR_MSGS[:type_error]%[pos.inspect, size.inspect]
  end

  if !source.respond_to? self.class::TO_SOURCE_METHOD
    raise TypeError, "Wrong source class #{source.class.name} for this class #{self.class.name}"
  end

  # Hash value retained to check its potential destructive change
  @hash = @source.hash
end
verbose() click to toggle source

Getter of the class instance variable @verbosity

# File lib/sub_object.rb, line 60
def self.verbose
  (defined? @verbosity) ? @verbosity : nil
end
verbose=(obj) click to toggle source

Setter of the class instance variable @@verbosity

# File lib/sub_object.rb, line 65
def self.verbose=(obj)
  @verbosity=obj
end

Public Instance Methods

<=>( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 175
def <=>(      *rest); to_source.send(__method__, *rest); end
==( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 165
def ==(       *rest); to_source.send(__method__, *rest); end
===( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 167
def ===(      *rest); to_source.send(__method__, *rest); end
=~( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 173
def =~(       *rest); to_source.send(__method__, *rest); end
inspect() click to toggle source

@return [String]

# File lib/sub_object.rb, line 182
def inspect
  warn_hash
  sprintf('%s[%d,%d]%s', self.class.name.split(/::/)[-1], @pos, @isize, @source[@pos, @isize].inspect)
end
Also aliased as: inspect_before_sub_object
inspect_before_sub_object()
Alias for: inspect
is_a?( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 169
def is_a?(    *rest); to_source.send(__method__, *rest); end
kind_of?(*rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 171
def kind_of?(*rest); to_source.send(__method__, *rest); end
method_missing(method_name, *rest, &block) click to toggle source

method_missing for any but destructive methods

@return [Object] @see respond_to_missing?

Calls superclass method
# File lib/sub_object.rb, line 191
def method_missing(method_name, *rest, &block)
  destructive_method?(method_name) ? super : to_source.send(method_name, *rest, &block)
end
pos_size() click to toggle source

@return [Aray]

# File lib/sub_object.rb, line 116
def pos_size
  [@pos, @isize]
end
respond_to_missing?(method_name, *rest) click to toggle source

Obligatory redefinition, following redefined {#method_missing}

Calls superclass method
# File lib/sub_object.rb, line 196
def respond_to_missing?(method_name, *rest)
  destructive_method?(method_name) ? super : to_source.send(:respond_to?, method_name, *rest)
end
source() click to toggle source

Frozen String returned.

# File lib/sub_object.rb, line 126
def source
  warn_hash
  src = @source.dup
  src.freeze
  src
end
subsize() click to toggle source

@return usually the size (Integer)

# File lib/sub_object.rb, line 121
def subsize
  @isize
end
to_s( *rest) click to toggle source

Redefining a method in Object to evaluate via {#to_source}

# File lib/sub_object.rb, line 177
def to_s(     *rest); to_source.send(__method__, *rest); end
to_source() click to toggle source

Returns the original representation of the instance as in the source

Each child class should set the constant TO_SOURCE_METHOD appropriately and should alias this method to the method registered to TO_SOURCE_METHOD . For example, for SubString,

TO_SOURCE_METHOD = :to_str
alias_method TO_SOURCE_METHOD, :to_source

Warning: DO NOT OVERWRITE THIS METHOD.

@return [Object]

# File lib/sub_object.rb, line 145
def to_source
  warn_hash
  @source[@pos, @isize].send(to_source_method)
end

Private Instance Methods

destructive_method?(method_name) click to toggle source
# File lib/sub_object.rb, line 204
def destructive_method?(method_name)
  met = method_name.to_s
  (/!$/ =~ met) || DESTRUCTIVE_METHODS.include?(met)
end
to_source_method() click to toggle source

Symbol of the method to get the original-like instance; e.g., :to_str for String. The value should be set in the child class, along with the crucial alias, e.g.,

TO_SOURCE_METHOD = :to_str
alias_method TO_SOURCE_METHOD, :to_source

@return [Symbol] Method name like :to_str

# File lib/sub_object.rb, line 54
def to_source_method
  self.class::TO_SOURCE_METHOD
end
warn_hash() click to toggle source
# File lib/sub_object.rb, line 210
def warn_hash
  klass = self.class
  verbosity_loc = (klass.instance_variable_defined?(:@verbosity) ? klass.instance_variable_get(:@verbosity) : nil)
  if ((verbosity_loc.nil? && !$VERBOSE.nil?) || verbosity_loc) && @hash != @source.hash
    str = @source[@pos, @isize].inspect
    # Suppresses the length only for warning (not the standard inspect), which would be printed repeatedly.
    str = str[0,60]+'..."' if str.size > 64
    # warn() would not run if $VERBOSE.nil? (in this case it should run if @verbosity is true).
    $stderr.printf("WARNING: source string has destructively changed: %s[%d,%d]%s\n", self.class.name, @pos, @isize, str)
  end
end