2003-12-16 00:44:25 -05:00
|
|
|
module RI
|
|
|
|
|
|
|
|
class ClassEntry
|
|
|
|
|
|
|
|
attr_reader :name
|
2004-08-30 10:22:26 -04:00
|
|
|
attr_reader :path_names
|
2003-12-16 00:44:25 -05:00
|
|
|
|
|
|
|
def initialize(path_name, name, in_class)
|
2004-08-30 10:22:26 -04:00
|
|
|
@path_names = [ path_name ]
|
2003-12-16 00:44:25 -05:00
|
|
|
@name = name
|
|
|
|
@in_class = in_class
|
|
|
|
@class_methods = []
|
|
|
|
@instance_methods = []
|
|
|
|
@inferior_classes = []
|
|
|
|
end
|
|
|
|
|
2004-08-30 10:22:26 -04:00
|
|
|
# We found this class in more tha one place, so add
|
|
|
|
# in the name from there.
|
|
|
|
def add_path(path)
|
|
|
|
@path_names << path
|
|
|
|
end
|
|
|
|
|
2003-12-16 00:44:25 -05:00
|
|
|
# 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"
|
2003-12-18 16:08:25 -05:00
|
|
|
internal_name = RiWriter.external_to_internal(external_name)
|
2003-12-16 00:44:25 -05:00
|
|
|
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)
|
2004-08-30 10:22:26 -04:00
|
|
|
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
|
2003-12-16 00:44:25 -05:00
|
|
|
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
|
|
|
|
|
2003-12-23 23:24:29 -05:00
|
|
|
def classes_and_modules
|
|
|
|
@inferior_classes
|
|
|
|
end
|
|
|
|
|
2003-12-18 16:08:25 -05:00
|
|
|
# Return an exact match to a particular name
|
|
|
|
def contained_class_named(name)
|
|
|
|
@inferior_classes.find {|c| c.name == name}
|
|
|
|
end
|
|
|
|
|
2003-12-16 00:44:25 -05:00
|
|
|
# return the list of local methods matching name
|
|
|
|
# We're split into two because we need distinct behavior
|
2003-12-23 23:24:29 -05:00
|
|
|
# when called from the _toplevel_
|
2003-12-16 15:28:44 -05:00
|
|
|
def methods_matching(name, is_class_method)
|
|
|
|
local_methods_matching(name, is_class_method)
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Find methods matching 'name' in ourselves and in
|
|
|
|
# any classes we contain
|
2003-12-16 15:28:44 -05:00
|
|
|
def recursively_find_methods_matching(name, is_class_method)
|
|
|
|
res = local_methods_matching(name, is_class_method)
|
2003-12-16 00:44:25 -05:00
|
|
|
@inferior_classes.each do |c|
|
2003-12-16 15:28:44 -05:00
|
|
|
res.concat(c.recursively_find_methods_matching(name, is_class_method))
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Return our full name
|
2003-12-18 22:58:57 -05:00
|
|
|
def full_name
|
2003-12-16 00:44:25 -05:00
|
|
|
res = @in_class.full_name
|
|
|
|
res << "::" unless res.empty?
|
|
|
|
res << @name
|
|
|
|
end
|
|
|
|
|
2004-03-24 14:17:42 -05:00
|
|
|
# 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
|
|
|
|
|
2003-12-16 00:44:25 -05:00
|
|
|
private
|
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
# 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)
|
2003-12-16 15:50:44 -05:00
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
list = case is_class_method
|
2003-12-16 15:50:44 -05:00
|
|
|
when nil then @class_methods + @instance_methods
|
2003-12-16 15:28:44 -05:00
|
|
|
when true then @class_methods
|
|
|
|
when false then @instance_methods
|
2003-12-18 22:58:57 -05:00
|
|
|
else fail "Unknown is_class_method: #{is_class_method.inspect}"
|
2003-12-16 15:28:44 -05:00
|
|
|
end
|
|
|
|
|
2003-12-18 16:08:25 -05:00
|
|
|
list.find_all {|m| m.name; m.name[name]}
|
2003-12-16 00:44:25 -05:00
|
|
|
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
|
2003-12-16 15:28:44 -05:00
|
|
|
def methods_matching(name, is_class_method)
|
|
|
|
res = recursively_find_methods_matching(name, is_class_method)
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def full_name
|
|
|
|
""
|
|
|
|
end
|
2003-12-18 16:08:25 -05:00
|
|
|
|
|
|
|
def module_named(name)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2003-12-16 00:44:25 -05:00
|
|
|
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
|