class RexleXPath

Public Class Methods

new(node=nil, debug: false) click to toggle source
# File lib/rexle-xpath.rb, line 23
def initialize(node=nil, debug: false)

  @node = node
  @debug = debug
  
end

Public Instance Methods

parse(s) click to toggle source
# File lib/rexle-xpath.rb, line 30
def parse(s)

  # it's an xpath function only
  if /^(?<name>\w+)\(\)$/ =~ s
    
    @node.method((name).to_sym).call
    
  # it's an element only
  elsif /^(?<name>\w+)$/ =~ s
    
    select(@node, [name])
    
  else

    xpath_instructions = RexleXPathParser.new(s).to_a
    
    query xpath_instructions, @node
  end
end
query(xpath_instructions=[], node=@node) click to toggle source
# File lib/rexle-xpath.rb, line 51
def query(xpath_instructions=[], node=@node)

  debug :query, node: node, xpath_instructions: xpath_instructions

  row = xpath_instructions.shift
  method_name, *args = row    

  return query xpath_instructions, node if row == :|
  
  if row.is_a? Array and row.first.is_a? Array then      
    
    unless row.any? {|x| x == :|} then
      
      return query row + xpath_instructions, node         
    else

      a = row

      a2 = a.inject([[]]) do |r, x|

        if x != :| then

          if r.last.is_a? Array then
            r.last << x
          else
            r << [x]
          end

        else
          r.unshift x
          r << []
        end
        r
      end

      name = a2.shift
      r3 =  method(name).call(a2, node)
      return r3
      
    end
  end

  result = if method_name == :predicate then

    result2 = method(method_name.to_sym).call node, args,[]
    
    if result2 and xpath_instructions.any? then
      query xpath_instructions, node
    else
      result2
    end
    
  else

    r3 = method(method_name.to_sym).call node, args, xpath_instructions
    r3
  end

  result.is_a?(Array) ? result.flatten : result

end

Private Instance Methods

attribute(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 122
def attribute(node, args, xpath_instructions)

  debug :attribute, args: args, node: node, 
      xpath_instructions: xpath_instructions
  
  key = args.first.is_a?(Array) ? args.first.first.to_sym : args.first.to_sym

  attr = node.attributes[key]
  
  xi = xpath_instructions

  if xi[0] and xi[0][0].to_sym == :value then

    _, operator, value = xi.shift
    attr.method(operator.to_sym).call value
  else
    attr ? Rexle::Element::Attribute.new(attr) : nil
  end
  
  
end
count(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 144
def count(node, args, xpath_instructions)    
  
  r = query xpath_instructions, node
  [r.length]
end
debug(method, h={}) click to toggle source
# File lib/rexle-xpath.rb, line 286
def debug(method, h={})
  
  return unless @debug
  
  puts
  puts '# inside ' + method.to_s
  h.each {|k,v| puts "... %s: %s" % [k,v.inspect] }
  puts
  
end
index(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 150
def index(node, args, xpath_instructions)    

  debug :index, node: node, args: args, 
      xpath_instructions: xpath_instructions    
  
  i = args.first.to_i
  r = query xpath_instructions, node

  [r[i-1]]
end
not(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 161
def not(node, args, xpath_instructions)
  
  r = query xpath_instructions, node
  !(r ? r.any? : r)
end
predicate(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 167
def predicate(node, args, xpath_instructions)
  
  debug :predicate, node: node, args: args, 
      xpath_instructions: xpath_instructions

  r = query args, node

  r.is_a?(Array) ? r.any? : r

end
recursive(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 178
def recursive(node, args, xpath_instructions)
  
  xi = args #xpath_instructions

  a = []
  a << query(xi.deep_clone, node)
  
  node.each_recursive {|e| a << query(xi.deep_clone, e) }
  
  a
end
select(node, args, xpath_instructions=[]) click to toggle source
# File lib/rexle-xpath.rb, line 190
def select(node, args, xpath_instructions=[])

  debug :select, node: node, args: args, 
      xpath_instructions: xpath_instructions

  selector = args.first
  
  nodes_found = if selector == '*' then
    node.elements.to_a
  elsif selector == '.'
    [node]
  else
    node.elements.select {|x| x.name == selector }
  end

  flat_xpi = xpath_instructions.flatten

  predicate = flat_xpi.first.to_s == 'predicate'
  
  if predicate and flat_xpi[1] == :index then
    
    i = flat_xpi[2].to_i - 1
    return nodes_found[i]
    
  end
  
  if xpath_instructions.any? and nodes_found.any? then

    nodes_found.inject([]) do |r, child_node| 

      # deep clone the xpath instructions
      xi = xpath_instructions.deep_clone
      operand = xi.shift  if xi.first == :|

      r2 = query(xi, child_node)

      r3 = case r2.class.to_s.to_sym
      when :'Rexle::Element' then r << r2
      when :TrueClass        
        predicate ? r << child_node : r << true
      when :FalseClass
        !predicate ? r << false : r
      when :'Rexle::Element::Attribute'
        r << child_node
      when :NilClass
        r
      else
        r2.any? ? r << r2 : r #<< child_node
      end # /case
      
      if operand == :| then
        nodes_found + r3
      else
        r3
      end
      
    end # /inject
    
  elsif xpath_instructions.any? and nodes_found.empty?
    
    operator = xpath_instructions.shift
    
    if operator == :| then
      query xpath_instructions, node
    else
      nodes_found
    end
    
  else
    nodes_found
  end  # /if
  
end
text(node, args, xpath_instructions)
Alias for: value
value(node, args, xpath_instructions) click to toggle source
# File lib/rexle-xpath.rb, line 264
def value(node, args, xpath_instructions)
  
  debug :value, node: node, args: args, 
      xpath_instructions: xpath_instructions
  
  
  
  operator, operand = args
  
  if operator then
    
    return false unless node.value
    node.value.method(operator.to_sym).call operand
    
  else
    [node.text]
  end
      
end
Also aliased as: text
|(args=[], node) click to toggle source
# File lib/rexle-xpath.rb, line 116
def |(args=[], node)
  r = args.flat_map {|x| query x, node}
  r.any?
end