module RI class ClassEntry attr_reader :name attr_reader :path_names def initialize(path_name, name, in_class) @path_names = [ path_name ] @name = name @in_class = in_class @class_methods = [] @instance_methods = [] @inferior_classes = [] end # We found this class in more tha one place, so add # in the name from there. def add_path(path) @path_names << path end # read in our methods and any classes # and modules in our namespace. Methods are # stored in files called name-c|i.yaml, # where the 'name' portion is the external # form of the method name and the c|i is a class|instance # flag def load_from(dir) Dir.foreach(dir) do |name| next if name =~ /^\./ # convert from external to internal form, and # extract the instance/class flag if name =~ /^(.*?)-(c|i).yaml$/ external_name = $1 is_class_method = $2 == "c" internal_name = RiWriter.external_to_internal(external_name) list = is_class_method ? @class_methods : @instance_methods path = File.join(dir, name) list << MethodEntry.new(path, internal_name, is_class_method, self) else full_name = File.join(dir, name) if File.directory?(full_name) inf_class = @inferior_classes.find {|c| c.name == name } if inf_class inf_class.add_path(full_name) else inf_class = ClassEntry.new(full_name, name, self) @inferior_classes << inf_class end inf_class.load_from(full_name) end end end end # Return a list of any classes or modules that we contain # that match a given string def contained_modules_matching(name) @inferior_classes.find_all {|c| c.name[name]} end def classes_and_modules @inferior_classes end # Return an exact match to a particular name def contained_class_named(name) @inferior_classes.find {|c| c.name == name} end # return the list of local methods matching name # We're split into two because we need distinct behavior # when called from the _toplevel_ def methods_matching(name, is_class_method) local_methods_matching(name, is_class_method) end # Find methods matching 'name' in ourselves and in # any classes we contain def recursively_find_methods_matching(name, is_class_method) res = local_methods_matching(name, is_class_method) @inferior_classes.each do |c| res.concat(c.recursively_find_methods_matching(name, is_class_method)) end res end # Return our full name def full_name res = @in_class.full_name res << "::" unless res.empty? res << @name end # Return a list of all out method names def all_method_names res = @class_methods.map {|m| m.full_name } @instance_methods.each {|m| res << m.full_name} res end private # Return a list of all our methods matching a given string. # Is +is_class_methods+ if 'nil', we don't care if the method # is a class method or not, otherwise we only return # those methods that match def local_methods_matching(name, is_class_method) list = case is_class_method when nil then @class_methods + @instance_methods when true then @class_methods when false then @instance_methods else fail "Unknown is_class_method: #{is_class_method.inspect}" end list.find_all {|m| m.name; m.name[name]} end end # A TopLevelEntry is like a class entry, but when asked to search # for methods searches all classes, not just itself class TopLevelEntry < ClassEntry def methods_matching(name, is_class_method) res = recursively_find_methods_matching(name, is_class_method) end def full_name "" end def module_named(name) end end class MethodEntry attr_reader :name attr_reader :path_name def initialize(path_name, name, is_class_method, in_class) @path_name = path_name @name = name @is_class_method = is_class_method @in_class = in_class end def full_name res = @in_class.full_name unless res.empty? if @is_class_method res << "::" else res << "#" end end res << @name end end # We represent everything know about all 'ri' files # accessible to this program class RiCache attr_reader :toplevel def initialize(dirs) # At the top level we have a dummy module holding the # overall namespace @toplevel = TopLevelEntry.new('', '::', nil) dirs.each do |dir| @toplevel.load_from(dir) end end end end