class FastRI::RiIndex

This class provides the same functionality as RiReader, with some improvements:

Some operations can be restricted to a given “scope”, that is, a “RI DB directory”. This allows you to e.g. look for all the instance methods in String defined by a package.

Such operations take a scope argument, which is either an integer which indexes the source in paths, or a name identifying the source (either “system” or a package name). If scope == nil, the information from all sources is merged.

Constants

MAGIC

Attributes

paths[R]

Public Class Methods

new_from_IO(anIO) click to toggle source
# File lib/fastri/ri_index.rb, line 209
def self.new_from_IO(anIO)
  obj = new
  obj.load(anIO)
  obj
end
new_from_paths(paths = nil) click to toggle source
# File lib/fastri/ri_index.rb, line 203
def self.new_from_paths(paths = nil)
  obj = new
  obj.rebuild_index(paths)
  obj
end

Public Instance Methods

all_names(scope = nil) click to toggle source

Return a list of all classes, modules, and methods.

# File lib/fastri/ri_index.rb, line 375
def all_names(scope = nil)
  full_class_names(scope).concat(full_method_names(scope))
end
dump(anIO) click to toggle source

Serializes index to the given IO.

# File lib/fastri/ri_index.rb, line 287
def dump(anIO)
  anIO.puts MAGIC
  anIO.puts "Sources:"
  @paths.zip(@gem_names).each{|p,g| anIO.puts "%-30s  %s" % [g, p]}
  anIO.puts "=" * 80
  anIO.puts "Namespaces:"
  anIO.puts @namespace_array
  anIO.puts "=" * 80
  anIO.puts "Methods:"
  anIO.puts @method_array
  anIO.puts "=" * 80
end
find_class_by_name(full_name, scope = nil) click to toggle source

Returns the ClassDescription associated to the given full_name.

# File lib/fastri/ri_index.rb, line 318
def find_class_by_name(full_name, scope = nil)
  entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
  return nil unless entry && entry.full_name == full_name
  get_class(entry)
end
find_method_by_name(full_name, scope = nil) click to toggle source

Returns the MethodDescription associated to the given full_name. Only the first definition is returned when scope = nil.

# File lib/fastri/ri_index.rb, line 326
def find_method_by_name(full_name, scope = nil)
  entry = get_entry(@method_array, full_name, MethodEntry, scope)
  return nil unless entry && entry.full_name == full_name
  get_method(entry)
end
find_methods(name, is_class_method, namespaces) click to toggle source

Returns an array of MethodEntry objects, corresponding to the methods in the ClassEntry objects in the namespaces array.

# File lib/fastri/ri_index.rb, line 334
def find_methods(name, is_class_method, namespaces)
  result = []
  namespaces.each do |ns|
    result.concat ns.methods_matching(name, is_class_method)
  end
  result
end
full_class_names(scope = nil) click to toggle source

Return the names of all classes and modules.

# File lib/fastri/ri_index.rb, line 365
def full_class_names(scope = nil)
  all_entries(@namespace_array, scope)
end
full_method_names(scope = nil) click to toggle source

Return the names of all methods.

# File lib/fastri/ri_index.rb, line 370
def full_method_names(scope = nil)
  all_entries(@method_array, scope)
end
get_class(class_entry) click to toggle source

Return a ClassDescription for a given ClassEntry.

# File lib/fastri/ri_index.rb, line 350
def get_class(class_entry)
  result = nil
  for path in class_entry.path_names
    path = RI::RiWriter.class_desc_path(path, class_entry)
    desc = File.open(path) {|f| RI::Description.deserialize(f) }
    if result
      result.merge_in(desc)
    else
      result = desc
    end
  end
  result
end
get_class_entry(full_name, scope = nil) click to toggle source

Returns the ClassEntry associated to the given full_name.

# File lib/fastri/ri_index.rb, line 392
def get_class_entry(full_name, scope = nil)
  entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
  return nil unless entry && entry.full_name == full_name
  entry
end
get_method(method_entry) click to toggle source

Return the MethodDescription for a given MethodEntry by deserializing the YAML.

# File lib/fastri/ri_index.rb, line 344
def get_method(method_entry)
  path = method_entry.path_name
  File.open(path) { |f| RI::Description.deserialize(f) }
end
get_method_entry(full_name, scope = nil) click to toggle source

Returns the MethodEntry associated to the given full_name.

# File lib/fastri/ri_index.rb, line 399
def get_method_entry(full_name, scope = nil)
  entry = get_entry(@method_array, full_name, MethodEntry, scope)
  return nil unless entry && entry.full_name == full_name
  entry
end
load(anIO) click to toggle source

Load the index from the given IO. It must contain a textual representation generated by dump.

# File lib/fastri/ri_index.rb, line 259
def load(anIO)
  header = anIO.gets    
  raise "Invalid format." unless header.chomp == MAGIC
  anIO.gets  # discard "Sources:"
  paths     = []
  gem_names = []
  until (line = anIO.gets).index("=" * 80) == 0
    gemname, path = line.strip.split(/\s+/)
    paths     << path
    gem_names << gemname
  end
  anIO.gets # discard "Namespaces:"
  namespace_array = []
  until (line = anIO.gets).index("=" * 80) == 0
    namespace_array << line
  end
  anIO.gets # discard "Methods:"
  method_array = []
  until (line = anIO.gets).index("=" * 80) == 0
    method_array << line
  end
  @paths           = paths
  @gem_names       = gem_names
  @namespace_array = namespace_array
  @method_array    = method_array
end
lookup_namespace_in(target, namespaces) click to toggle source

Returns an array of ClassEntry objects whose names match target, and which correspond to the namespaces contained in namespaces. namespaces is an array of ClassEntry objects.

# File lib/fastri/ri_index.rb, line 309
def lookup_namespace_in(target, namespaces)
  result = []
  namespaces.each do |ns|
    result.concat(ns.contained_modules_matching(target))
  end
  result
end
methods_under(class_entry_or_name, recursive, scope = nil) click to toggle source

Returns array of MethodEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy.

# File lib/fastri/ri_index.rb, line 446
def methods_under(class_entry_or_name, recursive, scope = nil)
  methods_under_matching(class_entry_or_name, //, recursive, scope)
end
methods_under_matching(class_entry_or_name, regexp, recursive, scope = nil) click to toggle source

Returns array of MethodEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy whose full_name matches the given regexp.

# File lib/fastri/ri_index.rb, line 453
def methods_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
  case class_entry_or_name
  when ClassEntry
    full_name = class_entry_or_name.full_name
  else
    full_name = class_entry_or_name
  end
  method_entry = get_entry(@method_array, full_name, MethodEntry)
  return [] unless method_entry
  ret = []
  re1, re2 = matching_regexps_method(full_name)
  (method_entry.index...@method_array.size).each do |i|
    entry = @method_array[i]
    break unless re1 =~ entry
    next if !recursive && re2 !~ entry 
    full_name = entry[/\S+/]
    next unless regexp =~ full_name
    if scope
      sources = method_sources(i)
      if sources.include?(sindex = scope_to_sindex(scope))
        ret << MethodEntry.new(self, full_name, i, sindex)
      end
    else
      ret << MethodEntry.new(self, full_name, i, nil)
    end
  end
  ret
end
namespaces_under(class_entry_or_name, recursive, scope = nil) click to toggle source

Returns array of ClassEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy.

# File lib/fastri/ri_index.rb, line 407
def namespaces_under(class_entry_or_name, recursive, scope = nil)
  namespaces_under_matching(class_entry_or_name, //, recursive, scope)
end
namespaces_under_matching(class_entry_or_name, regexp, recursive, scope = nil) click to toggle source

Returns array of ClassEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy whose full_name matches the given regexp.

# File lib/fastri/ri_index.rb, line 414
def namespaces_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
  case class_entry_or_name
  when ClassEntry
    class_entry = class_entry_or_name
  when ""
    class_entry = top_level_namespace(scope)[0]
  else
    class_entry = get_entry(@namespace_array, class_entry_or_name, ClassEntry, scope)
  end
  return [] unless class_entry
  ret = []
  re1, re2 = matching_regexps_namespace(class_entry.full_name)
  (class_entry.index+1...@namespace_array.size).each do |i|
    entry = @namespace_array[i]
    break unless re1 =~ entry
    next if !recursive && re2 !~ entry 
    full_name = entry[/\S+/]
    next unless regexp =~ full_name
    if scope
      sources = namespace_sources(i)
      if sources.include?(sindex = scope_to_sindex(scope))
        ret << ClassEntry.new(self, full_name, i, sindex)
      end
    else
      ret << ClassEntry.new(self, full_name, i, nil)
    end
  end
  ret
end
num_methods() click to toggle source

Returns the number of methods in the index.

# File lib/fastri/ri_index.rb, line 382
def num_methods
  @method_array.size
end
num_namespaces() click to toggle source

Returns the number of namespaces in the index.

# File lib/fastri/ri_index.rb, line 387
def num_namespaces
  @namespace_array.size
end
rebuild_index(paths = nil) click to toggle source
# File lib/fastri/ri_index.rb, line 215
  def rebuild_index(paths = nil)
    @paths = paths || RI::Paths::PATH
    @gem_names = paths.map do |p|
      fullp = File.expand_path(p)
      gemname = nil
      begin
        require 'rubygems'
        Gem.path.each do |gempath|
          re = %r^#{Regexp.escape(File.expand_path(gempath))}/doc/!
          if re =~ fullp
            gemname = fullp.gsub(re,"")[%r{^[^/]+}]
            break
          end
        end
      rescue LoadError
        # no RubyGems, no gems installed, skip it
      end
      gemname ? gemname : "system"
    end
    methods    = Hash.new{|h,k| h[k] = []}
    namespaces = methods.clone 
    @paths.each_with_index do |path, source_index|
      ri_reader = RI::RiReader.new(RI::RiCache.new(path))
      obtain_classes(ri_reader.top_level_namespace.first).each{|name| namespaces[name] << source_index }
      obtain_methods(ri_reader.top_level_namespace.first).each{|name| methods[name] << source_index }
    end
    @method_array = methods.sort_by{|h,k| h}.map do |name, sources|
      "#{name} #{sources.map{|x| x.to_s}.join(' ')}"
    end
    @namespace_array = namespaces.sort_by{|h,k| h}.map do |name, sources|
      "#{name} #{sources.map{|x| x.to_s}.join(' ')}"
    end

    puts "@method_array: #{@method_array.size}"
    puts "@namespace_array: #{@namespace_array.size}"
    puts @method_array.inject(0){|s,x| s + x.size}
    puts @namespace_array.inject(0){|s,x| s + x.size}
  end
source_paths_for(entry_or_name) click to toggle source

Returns array of Strings corresponding to the base directories of all the sources fo the given entry_or_name.

# File lib/fastri/ri_index.rb, line 484
def source_paths_for(entry_or_name)
  case entry_or_name
  when ClassEntry
    namespace_sources(entry_or_name.index).map{|i| @paths[i] }
  when MethodEntry
    method_sources(entry_or_name.index).map{|i| @paths[i]}
  when nil
    []
  else
    case entry_or_name
    when /[#.]\S+/
      method_entry = get_entry(@method_array, entry_or_name, MethodEntry, nil)
      source_paths_for(method_entry)
    when ""
      []
    else
      class_entry = get_entry(@namespace_array, entry_or_name, ClassEntry, nil)
      source_paths_for(class_entry)
    end
  end
end
top_level_namespace(scope = nil) click to toggle source

Returns an array with the top level namespace.

# File lib/fastri/ri_index.rb, line 302
def top_level_namespace(scope = nil)
  [TopLevelEntry.new(self, "", -1, scope ? scope_to_sindex(scope) : nil)]
end