class RbPath::Query

Public Class Methods

new(*query) click to toggle source

takes a string query or a pre-parsed query list

# File lib/rbpath/query.rb, line 6
def initialize(*query)
  @query = parse_query_list(query)
end

Public Instance Methods

parse_query_list(query) click to toggle source
# File lib/rbpath/query.rb, line 25
def parse_query_list(query)
  query.flat_map do |part|
    case part
    when String, Symbol
      parse_string_query(part.to_s)
    when Regexp
      {multi: false, neg: false, keys: [], regexp: part}
    else {multi: false, neg: false, keys: [part]}
    end
  end
end
parse_string_query(query) click to toggle source

Parsing rules:

  • query keys are seperated by spaces, keys with spaces must be single quoted

  • brackets group keys into an NOR group

  • parens group keys into a OR group

  • valid keys names consist of [chars|nums|spaces|-|_|.], anything else can be used as a seperator inside the parens/brackets

# File lib/rbpath/query.rb, line 17
def parse_string_query(query)
  query.scan(/(\([^\)]+\)|\[[^\]]+\]|'[^']+'|[^\s]+)/)
       .flatten
       .map { |keys| { multi: /\*\*/ === keys[0..1],
                       neg:  /[\[\*]/ === keys[0],
                       keys: keys.scan(/[\w\d\s\-\_\.]+/) }}
end
pquery(data) click to toggle source
# File lib/rbpath/query.rb, line 43
def pquery(data)
  do_query(deep_stringify_all(data), @query, [[]]).map(&:flatten)
end
query(data) click to toggle source
# File lib/rbpath/query.rb, line 37
def query(data)
  data = deep_stringify_all(data)
  do_query(data, @query, [[]]).map(&:flatten)
                              .map { |path| get_value(data, path) }
end
values_at(data, paths) click to toggle source
# File lib/rbpath/query.rb, line 47
def values_at(data, paths)
  paths.map {|path| get_value(deep_stringify_all(data), path) }
end

Private Instance Methods

all_keys(data, path) click to toggle source
# File lib/rbpath/query.rb, line 78
def all_keys(data, path)
  value = get_value(data, path)

  case value
  when Hash  then value.keys
  when Array then (0...value.size).map(&:to_s)
  when RbPath
    value.rbpath_fields
  else [value]
  end
end
do_query(data, query, valid_paths) click to toggle source
# File lib/rbpath/query.rb, line 53
def do_query(data, query, valid_paths)
  matcher, *rest = *query

  if query.empty? || valid_paths.empty?
    valid_paths
  elsif matcher[:multi]
     do_query(data, rest, valid_paths) +
       do_query(data, query, match(data, matcher, valid_paths))
  else
    do_query(data, rest, match(data, matcher, valid_paths))
  end
end
get_value(data, path) click to toggle source
# File lib/rbpath/query.rb, line 90
def get_value(data, path)
  return data if path.empty?
  key, *rest = *path

  case data
  when Hash
    get_value(data[key], rest)
  when Array
    get_value(data[Integer(key)], rest) rescue nil
  when RbPath
    get_value(data.send(key), rest) if data.respond_to?(key)
  when key
    rest.empty? ? data : nil
  end
end
match(data, matcher, valid_paths) click to toggle source
# File lib/rbpath/query.rb, line 66
def match(data, matcher, valid_paths)
  neg, keys, rgx = matcher.values_at(:neg, :keys, :regexp)

  valid_paths.flat_map { |path| children = if rgx
                                    all_keys(data, path).grep(rgx)
                                  elsif neg
                                    (all_keys(data, path) - keys)
                                  else keys; end
                                [path].product(children).map(&:flatten) } \
             .select   { |path| get_value(data, path) }
end