class Infoboxer::WikiPath

@private

Constants

ParseError

Public Class Methods

_parse(string) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 9
def _parse(string)
  scanner = StringScanner.new(string)
  res = []
  loop do
    res << scan_step(scanner)
    break if scanner.eos?
  end
  res
end
new(path) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 71
def initialize(path)
  @path = path
end
parse(string) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 19
def parse(string)
  new(_parse(string))
end

Private Class Methods

process_type(type) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 56
def process_type(type)
  type.gsub(/(?:^|_)([a-z])/, &:upcase).tr('_', '').to_sym
      .tap { |t| valid_type?(t) or fail(ParseError, "Unrecognized node type: #{type}") }
end
process_value(value) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 45
def process_value(value)
  case value
  when /^'(.*)'$/, /^"(.*)"$/
    Regexp.last_match(1)
  when %r{^/(.+)/$}
    Regexp.new(Regexp.last_match(1))
  else
    value
  end
end
scan_step(scanner) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 25
def scan_step(scanner) # rubocop:disable Metrics/PerceivedComplexity
  op = scanner.scan(%r{//?}) or unexpected(scanner, '/')
  type = scanner.scan(/[A-Za-z_]*/)
  attrs = {}
  while scanner.scan(/\[/)
    attr = scanner.scan(/[-a-z_0-9]+/) or unexpected(scanner, 'attribute name')
    if scanner.scan(/\]/)
      (attrs[:predicates] ||= []) << "#{attr}?".to_sym
      next
    end
    scanner.scan(/\s*=\s*/) or unexpected(scanner, '= or ]')
    value = scanner.scan(/[^\]]+/) # TODO: probably, should do a proper [] counting?..
    scanner.scan(/\]/) or unexpected(scanner, ']')
    attrs[attr.to_sym] = process_value(value)
  end
  res = op == '//' ? {op: :lookup} : {}
  res[:type] = process_type(type) unless type.empty?
  res.merge(attrs) # TODO: raise if empty selector
end
unexpected(scanner, expected) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 65
def unexpected(scanner, expected)
  place = scanner.eos? ? 'end of pattern' : scanner.rest.inspect
  fail ParseError, "Unexpected #{place}, expecting #{expected}"
end
valid_type?(t) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 61
def valid_type?(t)
  t == :Section || Infoboxer::Tree.const_defined?(t)
end

Public Instance Methods

call(node) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 75
def call(node)
  @path.inject(node) { |res, step| apply_step(res, step) }
end

Private Instance Methods

apply_step(node, step) click to toggle source
# File lib/infoboxer/wiki_path.rb, line 81
def apply_step(node, step)
  # TODO: "compile" the op/args sequences at WikiPath initialization
  step = step.dup
  op = step.delete(:op) || :lookup_children
  args = []
  if (t = step.delete(:type))
    args << t
  end
  if (pred = step.delete(:predicates))
    args.concat(pred)
  end
  args << step unless step.empty?
  node.send(op, *args)
end