class Path

Attributes

original[R]

Public Class Methods

make(path_or_array_or_string) click to toggle source
# File lib/dense/path.rb, line 28
def make(path_or_array_or_string)

  case (pas = path_or_array_or_string)
  when nil then nil
  when Dense::Path then pas
  when Array then make_from_array(pas)
  when String then Dense::Path.new(pas)
  else
    fail ArgumentError.new(
      "cannot make Dense::Path instance out of #{pas.inspect}")
  end
end
new(s) click to toggle source
# File lib/dense/path.rb, line 6
  def initialize(s)

    @original = s

    fail ArgumentError.new(
      "argument is a #{s.class}, not a String"
    ) unless s.is_a?(String)

    s = ".#{s}" \
      unless s[0, 1] == '[' || s[0, 2] == '.['

#Raabro.pp(Parser.parse(s, debug: 3), colors: true)
    @path = Parser.parse(s)

#Raabro.pp(Parser.parse(s, debug: 3), colors: true) unless @path
    fail ArgumentError.new(
      "couldn't determine path from #{s.inspect}"
    ) unless @path
  end

Protected Class Methods

make_from_array(path_array) click to toggle source
# File lib/dense/path.rb, line 55
def make_from_array(path_array)

  symbolize!(path_array)
  validate(path_array)

  path = Dense::Path.allocate
  path.instance_eval { @path = path_array }
  path.instance_eval { @original = path.to_s }

  path
end
symbolize!(o) click to toggle source
# File lib/dense/path.rb, line 43
def symbolize!(o)

  if o.is_a?(Array)
    o
      .each { |e| symbolize!(e) }
  elsif o.is_a?(Hash)
    o.keys
      .select { |k| k.is_a?(String) }
      .each { |k| o[k.to_sym] = o.delete(k) }
  end
end
validate(path_array) click to toggle source
# File lib/dense/path.rb, line 67
def validate(path_array)

  path_array
    .each_with_index { |elt, i|
      (elt.is_a?(Array) ? elt : [ elt ])
        .each { |e| validate_path_elt(e, i) } }

  true
end
validate_path_elt(elt, i) click to toggle source
# File lib/dense/path.rb, line 77
def validate_path_elt(elt, i)

  return if elt.is_a?(Integer)
  return if elt.is_a?(String)
  return if [ :dot, :dotstar, :star ].include?(elt)

  if elt.is_a?(Hash)
    ks = elt.keys.sort
    return if ks == [ :end, :start, :step ]
    return if ks == [ :count, :start ]
  end

  fail TypeError.new("not a path element (@#{i}): #{elt.inspect}")
end

Public Instance Methods

-(path) click to toggle source
# File lib/dense/path.rb, line 151
def -(path)

  self.class.make(subtract(@path.dup, path.to_a.dup))
end
==(other) click to toggle source
# File lib/dense/path.rb, line 145
def ==(other)

  other.class == self.class &&
  other.to_a == @path
end
[](offset, count=nil) click to toggle source
# File lib/dense/path.rb, line 134
def [](offset, count=nil)

  if count == nil && offset.is_a?(Integer)
    @path[offset]
  elsif count
    self.class.make(@path[offset, count])
  else
    self.class.make(@path[offset])
  end
end
any?() click to toggle source
# File lib/dense/path.rb, line 111
def any?; @path.any?; end
empty?() click to toggle source
# File lib/dense/path.rb, line 112
def empty?; @path.empty?; end
first() click to toggle source
# File lib/dense/path.rb, line 114
def first; @path.first; end
gather(data) click to toggle source
# File lib/dense/path.rb, line 161
def gather(data)

  _gather(0, [], nil, data, @path, [])
    .inject({}) { |h, hit| h[(hit[1] + [ hit[3] ]).inspect] ||= hit; h }
    .values
end
last() click to toggle source
# File lib/dense/path.rb, line 115
def last; @path.last; end
length() click to toggle source
# File lib/dense/path.rb, line 108
def length; @path.length; end
Also aliased as: size
multiple?() click to toggle source
# File lib/dense/path.rb, line 98
def multiple?

  ! single?
end
narrow(outcome) click to toggle source
# File lib/dense/path.rb, line 156
def narrow(outcome)

  single? ? outcome.first : outcome
end
pop() click to toggle source
# File lib/dense/path.rb, line 117
def pop; @path.pop; end
shift() click to toggle source
# File lib/dense/path.rb, line 118
def shift; @path.shift; end
single?() click to toggle source
# File lib/dense/path.rb, line 93
def single?

  ! @path.find { |e| e.is_a?(Symbol) || e.is_a?(Hash) || e.is_a?(Array) }
end
size()
Alias for: length
to_a() click to toggle source
# File lib/dense/path.rb, line 103
def to_a

  @path
end
to_s() click to toggle source
# File lib/dense/path.rb, line 120
def to_s

  o = StringIO.new

  @path.each { |e|
    s = _to_s(e, false)
    o << '.' unless o.size == 0 || '[.'.index(s[0, 1])
    o << s }

  s = o.string

  s[0, 2] == '..' ? s[1..-1] : s
end

Protected Instance Methods

_dot_gather(depth, path0, data0, data, path, acc) click to toggle source
# File lib/dense/path.rb, line 237
  def _dot_gather(depth, path0, data0, data, path, acc)

#ind = '  ' * depth
#puts ind + "+--- _dot_gather()"
#puts ind + "| path0: #{path0.inspect}"
#puts ind + "| data: #{data.inspect}"
#puts ind + "| depth: #{depth} / path: #{path.inspect}"

    a = _gather(depth, path0, data0, data, path, []).select { |r| r.first }
    return acc.concat(a) if a.any?

    keys = _keys(data)

    return acc unless keys

    keys.each { |k|
      _dot_gather(depth + 1, path0 + [ k ], data, data[k], path, acc) }

    acc
  end
_gather(depth, path0, data0, data, path, acc) click to toggle source
# File lib/dense/path.rb, line 277
  def _gather(depth, path0, data0, data, path, acc)

    k = path.first
#ind = '  ' * depth
#col = [ LG, DG, LB ][depth % 3]
#print col
#puts ind + "+--- _gather()"
#puts ind + "| path0: #{path0.inspect}"
#puts ind + "| data: #{data.inspect}"
#puts ind + "| depth: #{depth} / path: #{path.inspect}"
#puts ind + "| k: " + k.inspect

#puts RD + ind + "| 0-> " + [ false, path0[0..-2], data0, path0.last, path ].inspect if data.nil? && ! _has_key?(data0, path0.last)
    return acc.push([ false, path0[0..-2], data0, path0.last, path ]) \
      if data.nil? && ! _has_key?(data0, path0.last)

#puts GN + ind + "| 1-> " + [ true, path0[0..-2], data0, path0.last ].inspect if k.nil?
    return acc.push([ true, path0[0..-2], data0, path0.last ]) \
      if k.nil?

#puts RD + ind + "| 2-> " + [ false, path0[0..-2], data0, path0.last, path ].inspect unless data.is_a?(Array) || data.is_a?(Hash)
    return acc.push([ false, path0, data, path.first, path[1..-1] ]) \
      unless data.is_a?(Array) || data.is_a?(Hash)

    return _dot_gather(depth, path0, data0, data, path[1..-1], acc) \
      if k == :dot

#puts col + ind + "| stars:\n" + _stars(data0, data, k).collect(&:first).to_pp if k == :star || k == :dotstar
    return _stars(data0, data, k).inject(acc) { |a, (pa, da0, da)|
      _gather(depth + 1, path0 + pa, da0, da, path[1..-1], a)
    } if k == :star || k == :dotstar

    keys = _resolve_keys(data, k)
#puts col + ind + "| keys: " + keys.inspect

    keys.inject(acc) { |a, kk|
      _gather(
        depth + 1, path0 + [ kk ], data, _index(data, kk), path[1..-1], a) }
  end
_has_key?(data, key) click to toggle source
# File lib/dense/path.rb, line 267
def _has_key?(data, key)

  if data.is_a?(Array)
    l = data.length
    key.is_a?(Integer) && (key < 0 ? (key >= -l) : (key < l))
  else
    data.has_key?(key)
  end
end
_index(o, k) click to toggle source
# File lib/dense/path.rb, line 258
def _index(o, k)

  case o
  when Array then k.is_a?(Integer) ? o[k] : nil
  when Hash then o[k]
  else nil
  end
end
_keys(o) click to toggle source
# File lib/dense/path.rb, line 170
def _keys(o)

  return (0..o.length - 1).to_a if o.is_a?(Array)
  return o.keys if o.is_a?(Hash)
  nil
end
_resolve_hash_key(o, k) click to toggle source
# File lib/dense/path.rb, line 177
def _resolve_hash_key(o, k)

  return [ nil ] unless o.is_a?(Array)

  be = k[:start] || 0

  en, st =
    if co = k[:count]
      [ be + co - 1, 1 ]
    else
      [ k[:end] || o.length - 1, k[:step] || 1 ]
    end

  Range.new(be, en).step(st).to_a
end
_resolve_key(o, k) click to toggle source
# File lib/dense/path.rb, line 193
def _resolve_key(o, k)

  return _resolve_hash_key(o, k) if k.is_a?(Hash)

  if o.is_a?(Hash)

    return o.keys.select { |ok| ok =~ k } if k.is_a?(Regexp)
    [ k.to_s ]

  else # o is a Array

    case k
    when /\Afirst\z/i then [ 0 ]
    when /\Alast\z/i then [ -1 ]
    else [ k ]
    end
  end
end
_resolve_keys(o, k) click to toggle source
# File lib/dense/path.rb, line 212
def _resolve_keys(o, k)

  ks = k.is_a?(Hash) ? [ k ] : Array(k)
  ks = ks.inject([]) { |a, kk| a.concat(_resolve_key(o, kk)) }
end
_rex_to_s(r) click to toggle source
# File lib/dense/path.rb, line 366
def _rex_to_s(r)

  r.fixed_encoding? ?
    r.inspect + (Dense::Path::Parser::R_ENCODINGS[r.encoding.name] || '') :
    r.inspect
end
_stars(data0, data, key, path=[], acc=[]) click to toggle source
# File lib/dense/path.rb, line 218
  def _stars(data0, data, key, path=[], acc=[])

#p [ :_stars, key, path, data0, data ]
    acc.push([ path, data0, data ]) if path.any?

    return acc unless data.is_a?(Hash) || data.is_a?(Array)

    return acc unless key
    key = key == :dotstar ? key : nil

    if data.is_a?(Array)
      data.each_with_index { |e, i| _stars(data, e, key, path + [ i ], acc) }
    else
      data.each { |k, v| _stars(data, v, key, path + [ k ], acc) }
    end

    acc
  end
_str_to_s(elt, in_array) click to toggle source
# File lib/dense/path.rb, line 355
def _str_to_s(elt, in_array)

  return elt.inspect if in_array

  s = elt.to_s

  return "\\#{s}" if s == '.' || s == '*'
  return "[#{elt.inspect}]" if s =~ /["']/
  s
end
_to_s(elt, in_array) click to toggle source
# File lib/dense/path.rb, line 327
def _to_s(elt, in_array)

  case elt
  when Hash
    s =
      if elt[:count]
        [ elt[:start], elt[:count] ].collect(&:to_s).join(',')
      else
        [ "#{elt[:start]}:#{elt[:end]}", elt[:step] ].compact.join(':')
      end
    in_array ? s : "[#{s}]"
  when Array
    "[#{elt.map { |e| _to_s(e, true) }.join(';')}#{elt.size < 2 ? ';' : ''}]"
  when String
    _str_to_s(elt, in_array)
  when Regexp
    _rex_to_s(elt)
  when :star
    '*'
  when :dot
    '.'
  when :dotstar
    '..*'
  else
    elt.to_s
  end
end
subtract(apath0, apath1) click to toggle source
# File lib/dense/path.rb, line 317
def subtract(apath0, apath1)

  while apath0.any? && apath1.any? && apath0.last == apath1.last
    apath0.pop
    apath1.pop
  end

  apath0
end