class MatchSkeleton

Class MatchSkeleton

To represent {MatchData} with much less memory use

Attributes

pos_begin[RW]

The position {Regexp} match has started. For example, both

/x/.match('0000x', 0)
/x/.match('0000x', 3)

give the same (equal) {MatchData}. This instance variable {#pos_begin} holds the position (0 or 3 in the cases above), if set explicitly.

regexp[R]

The same as {MatchData#regexp}.

string[R]

The same as {MatchData#string} but it is identical to the original string. If the original string is modified destructively, this too is modified.

Public Class Methods

new(md, string=nil, pos_begin: nil) click to toggle source

Constructor

If the second argument is omitted, it is taken from the first argument with +MatchData#string+. However, it would spoil the whole point of using this class, given +MatchData#string+ always “dups” the original string and uses up extra memory space! If you do not specify the second argument, this class offers almost identical functions but works slower, save from the bonus information of pos_begin, which can be added in this initialization in this class.

The point of using this class is to save the memory when you make multiple applications with many Regexp to the identical instance of String (with the same object_id) and if you want to keep the results of the Regexp match as MatchData or equivalent. So, do not forget to specify the second argument in this initialization!

@param md [MatchData] @param string [String] If not specified, it is taken from the first argument. @param pos_begin: [Integer] The position where {Regexp} match has started.

# File lib/match_skeleton.rb, line 53
def initialize(md, string=nil, pos_begin: nil)
  size_str = md.string.size
  if string && string.size != size_str
    raise ArgumentError, 'The first parameter is obligatory.'
  end
  @string = (string || md.string)
  @regexp = md.regexp
  @pre_match  = (0...md.pre_match.size)       # {Range}
  @post_match = ((size_str-md.post_match.size)...size_str)    # {Range}

  # @offsets is Hash of Range-s with the keys of both Integer and possibly Symbol
  # if names exist.
  names  = md.names
  ar_off = (0..(md.size-1)).map do |n|
    ar = md.offset(n)
    (ar.first...ar.last)
  end
  @offsets = {}
  ar_off.each_with_index do |ev, ei|
    @offsets[ei] = ev
    ej = ei - 1
    @offsets[names[ej]] = ev if (ej >= 0 && names[ej])
  end

  @pos_begin = pos_begin
end

Public Instance Methods

==(obj) click to toggle source

Comparable with {MatchSkeleton} and {MatchData}

A difference in {#pos_begin} is not taken into account.

@param obj [Object] The methods of {#string}, {#regexp}, {#pre_match} have to be defined and return the same to be true. Practically, only {MatchSkeleton} and {MatchData} may return true. @return [Boolean] @see eql?

# File lib/match_skeleton.rb, line 87
def ==(obj)
  !!((defined?(obj.string)     && string     == obj.string) &&
     (defined?(obj.regexp)     && regexp     == obj.regexp) &&
     (defined?(obj.pre_match)  && pre_match  == obj.pre_match) &&
     (defined?(obj.post_match) && post_match == obj.post_match))
  # nb., defined?() can return nil, and then nil (not false) will be returned.
end
[](i, j=nil) click to toggle source

The same as {MatchData#[]}

@param i [Integer, Range, Symbol, String] @param j [Integer, NilClass] @return [String, Array, NilClass] @raise [IndexError] if an invalid argument(s) is given.

# File lib/match_skeleton.rb, line 101
def [](i, j=nil)
  if j
    to_a[i, j]
  elsif defined?(i.to_sym)
    i = i.to_s
    raise IndexError, sprintf("undefined group name reference: %s", i) if !names.include?(i)
    offset2string(i)
  else
    to_a[i]
  end
end
begin(n) click to toggle source

The same as {MatchData#begin}

@param n [Integer] @return [Integer]

# File lib/match_skeleton.rb, line 117
def begin(n)
  offset(n)[0]
end
captures() click to toggle source

The same as {MatchData#captures}

@return [Array]

# File lib/match_skeleton.rb, line 124
def captures
  to_a[1..-1]
end
end(n) click to toggle source

The same as {MatchData#end}

@param n [Integer] @return [Integer]

# File lib/match_skeleton.rb, line 132
def end(n)
  offset(n)[1]
end
inspect() click to toggle source

Similar to {MatchData#inspect}

@return [String]

# File lib/match_skeleton.rb, line 156
def inspect
  core = ''
  ar = (names.empty? ? captures : names)
  ar.each_with_index do |ev, ei|
    core << sprintf(" %d:%s", ei, ev.inspect)
  end
  sprintf("#<%s %s%s>", self.class.to_s, self[0].inspect, core)
end
length()
Alias for: size
names() click to toggle source

The same as {MatchData#names} and {Regexp#names}

@return [Array<String>]

# File lib/match_skeleton.rb, line 168
def names
  regexp.names
end
offset(n) click to toggle source

The same as {MatchData#offset}

Due to the change in Ruby 2.6 rubyreferences.github.io/rubychanges/2.6.html#endless-range-1 the old routines would raise an Excetion when the end range is nil, that is, there is no match for MatchData#, hence needed updating.

@param n [Integer] @return [Array<integer>]

# File lib/match_skeleton.rb, line 181
def offset(n)
  if defined?(n.to_sym)
    n = n.to_s 
    raise IndexError, sprintf("undefined group name reference: %s", n) if !names.include?(n)
  end

  ## This used to work before Ruby 2.6
  # [@offsets[n].first,
  #  @offsets[n].last]

  [:first, :last].map do |ec|
    begin
      @offsets[n].public_send ec
    rescue RangeError
      nil
    end
  end
end
post_match() click to toggle source

The same as {MatchData#post_match}

@return [String]

# File lib/match_skeleton.rb, line 210
def post_match
  @string[@post_match]
end
pre_match() click to toggle source

The same as {MatchData#pre_match}

@return [String]

# File lib/match_skeleton.rb, line 203
def pre_match
  @string[@pre_match]
end
size() click to toggle source

The same as {MatchData#size}

@return [Integer]

# File lib/match_skeleton.rb, line 217
def size
  to_a.size
end
Also aliased as: length
to_a() click to toggle source

The same as {MatchData#to_a}

@return [Array]

# File lib/match_skeleton.rb, line 225
def to_a
  indices = @offsets.keys.sort
  indices.delete_if { |i| !defined?(i.divmod) }
  indices.map { |i| offset2string(i) }
end
to_s() click to toggle source

The same as {MatchData#to_s}

@return [String]

# File lib/match_skeleton.rb, line 234
def to_s
  self[0]
end
values_at(*rest) click to toggle source

The same as {MatchData#values_at}

@param *rest [Integer, Symbol, String] @return [Array]

# File lib/match_skeleton.rb, line 242
def values_at(*rest)
  locary = to_a
  rest.map do |i|
    locary[i]
  end
end

Private Instance Methods

offset2string(i) click to toggle source

@note Due to the change in Ruby 2.6, k.last may raise RangeError.

In this instance, k.first must be always nil when k.last is nil.
So, k.last would not be evaluated when k.last is nil.
But it is playing safe (by adding "rescue").
# File lib/match_skeleton.rb, line 257
def offset2string(i)
  k = @offsets[i]
  (k.first && (k.last rescue nil)) ? string[k] : nil
end