Class | RDoc::RI::Driver |
In: |
ri/driver.rb
|
Parent: | Object |
# File ri/driver.rb, line 204 204: def initialize(options) 205: @names = options[:names] 206: 207: @class_cache_name = 'classes' 208: @all_dirs = RDoc::RI::Paths.path(true, true, true, true) 209: @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first 210: @homepath = @homepath.sub(/\.rdoc/, '.ri') 211: @sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false) 212: 213: FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path 214: 215: @class_cache = nil 216: 217: @display = RDoc::RI::DefaultDisplay.new(options[:formatter], 218: options[:width], 219: options[:use_stdout]) 220: end
# File ri/driver.rb, line 14 14: def self.process_args(argv) 15: options = {} 16: options[:use_stdout] = !$stdout.tty? 17: options[:width] = 72 18: options[:formatter] = RDoc::RI::Formatter.for 'plain' 19: options[:list_classes] = false 20: options[:list_names] = false 21: 22: # By default all paths are used. If any of these are true, only those 23: # directories are used. 24: use_system = false 25: use_site = false 26: use_home = false 27: use_gems = false 28: doc_dirs = [] 29: 30: opts = OptionParser.new do |opt| 31: opt.program_name = File.basename $0 32: opt.version = RDoc::VERSION 33: opt.summary_indent = ' ' * 4 34: 35: directories = [ 36: RDoc::RI::Paths::SYSDIR, 37: RDoc::RI::Paths::SITEDIR, 38: RDoc::RI::Paths::HOMEDIR 39: ] 40: 41: if RDoc::RI::Paths::GEMDIRS then 42: Gem.path.each do |dir| 43: directories << "#{dir}/doc/*/ri" 44: end 45: end 46: 47: opt.banner = "Usage: \#{opt.program_name} [options] [names...]\n\nWhere name can be:\n\nClass | Class::method | Class#method | Class.method | method\n\nAll class names may be abbreviated to their minimum unambiguous form. If a name\nis ambiguous, all valid options will be listed.\n\nThe form '.' method matches either class or instance methods, while #method\nmatches only instance and ::method matches only class methods.\n\nFor example:\n\n\#{opt.program_name} Fil\n\#{opt.program_name} File\n\#{opt.program_name} File.new\n\#{opt.program_name} zip\n\nNote that shell quoting may be required for method names containing\npunctuation:\n\n\#{opt.program_name} 'Array.[]'\n\#{opt.program_name} compact\\\\!\n\nBy default ri searches for documentation in the following directories:\n\n\#{directories.join \"\\n \"}\n\nSpecifying the --system, --site, --home, --gems or --doc-dir options will\nlimit ri to searching only the specified directories.\n\nOptions may also be set in the 'RI' environment variable.\n" 48: 49: opt.separator nil 50: opt.separator "Options:" 51: opt.separator nil 52: 53: opt.on("--classes", "-c", 54: "Display the names of classes and modules we", 55: "know about.") do |value| 56: options[:list_classes] = value 57: end 58: 59: opt.separator nil 60: 61: opt.on("--doc-dir=DIRNAME", "-d", Array, 62: "List of directories to search for", 63: "documentation. If not specified, we search", 64: "the standard rdoc/ri directories. May be", 65: "repeated.") do |value| 66: value.each do |dir| 67: unless File.directory? dir then 68: raise OptionParser::InvalidArgument, "#{dir} is not a directory" 69: end 70: end 71: 72: doc_dirs.concat value 73: end 74: 75: opt.separator nil 76: 77: opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", 78: RDoc::RI::Formatter::FORMATTERS.keys, 79: "Format to use when displaying output:", 80: " #{RDoc::RI::Formatter.list}", 81: "Use 'bs' (backspace) with most pager", 82: "programs. To use ANSI, either disable the", 83: "pager or tell the pager to allow control", 84: "characters.") do |value| 85: options[:formatter] = RDoc::RI::Formatter.for value 86: end 87: 88: opt.separator nil 89: 90: unless RDoc::RI::Paths::GEMDIRS.empty? then 91: opt.on("--[no-]gems", 92: "Include documentation from RubyGems.") do |value| 93: use_gems = value 94: end 95: end 96: 97: opt.separator nil 98: 99: opt.on("--[no-]home", 100: "Include documentation stored in ~/.rdoc.") do |value| 101: use_home = value 102: end 103: 104: opt.separator nil 105: 106: opt.on("--[no-]list-names", "-l", 107: "List all the names known to RDoc, one per", 108: "line.") do |value| 109: options[:list_names] = value 110: end 111: 112: opt.separator nil 113: 114: opt.on("--no-pager", "-T", 115: "Send output directly to stdout.") do |value| 116: options[:use_stdout] = !value 117: end 118: 119: opt.separator nil 120: 121: opt.on("--[no-]site", 122: "Include documentation from libraries", 123: "installed in site_lib.") do |value| 124: use_site = value 125: end 126: 127: opt.separator nil 128: 129: opt.on("--[no-]system", 130: "Include documentation from Ruby's standard", 131: "library.") do |value| 132: use_system = value 133: end 134: 135: opt.separator nil 136: 137: opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger, 138: "Set the width of the output.") do |value| 139: options[:width] = value 140: end 141: end 142: 143: argv = ENV['RI'].to_s.split.concat argv 144: 145: opts.parse! argv 146: 147: options[:names] = argv 148: 149: options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home, 150: use_gems, *doc_dirs) 151: options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site, 152: use_home, use_gems, *doc_dirs) 153: 154: options 155: 156: rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e 157: puts opts 158: puts 159: puts e 160: exit 1 161: end
# File ri/driver.rb, line 198 198: def self.run(argv = ARGV) 199: options = process_args argv 200: ri = new options 201: ri.run 202: end
# File ri/driver.rb, line 252 252: def cache_file_for(klassname) 253: File.join cache_file_path, klassname.gsub(/:+/, "-") 254: end
# File ri/driver.rb, line 222 222: def class_cache 223: return @class_cache if @class_cache 224: 225: newest = map_dirs('created.rid', :all) do |f| 226: File.mtime f if test ?f, f 227: end.max 228: 229: up_to_date = (File.exist?(class_cache_file_path) and 230: newest < File.mtime(class_cache_file_path)) 231: 232: @class_cache = if up_to_date then 233: load_cache_for @class_cache_name 234: else 235: class_cache = {} 236: 237: classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] } 238: populate_class_cache class_cache, classes 239: 240: classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } 241: warn "Updating class cache with #{classes.size} classes..." 242: 243: populate_class_cache class_cache, classes, true 244: write_cache class_cache, class_cache_file_path 245: end 246: end
# File ri/driver.rb, line 248 248: def class_cache_file_path 249: File.join cache_file_path, @class_cache_name 250: end
# File ri/driver.rb, line 260 260: def display_class(name) 261: klass = class_cache[name] 262: @display.display_class_info klass, class_cache 263: end
# File ri/driver.rb, line 265 265: def load_cache_for(klassname) 266: path = cache_file_for klassname 267: 268: if File.exist? path and 269: File.mtime(path) >= File.mtime(class_cache_file_path) then 270: File.open path, 'rb' do |fp| 271: Marshal.load fp.read 272: end 273: else 274: class_cache = nil 275: 276: File.open class_cache_file_path, 'rb' do |fp| 277: class_cache = Marshal.load fp.read 278: end 279: 280: klass = class_cache[klassname] 281: return nil unless klass 282: 283: method_files = klass["sources"] 284: cache = {} 285: 286: sys_dir = @sys_dirs.first 287: method_files.each do |f| 288: system_file = f.index(sys_dir) == 0 289: Dir[File.join(File.dirname(f), "*")].each do |yaml| 290: next unless yaml =~ /yaml$/ 291: next if yaml =~ /cdesc-[^\/]+yaml$/ 292: method = read_yaml yaml 293: name = method["full_name"] 294: ext_path = f 295: ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)% 296: method["source_path"] = ext_path unless system_file 297: cache[name] = method 298: end 299: end 300: 301: write_cache cache, path 302: end 303: end
# File ri/driver.rb, line 305 305: def map_dirs(file_name, system=false) 306: dirs = if system == :all then 307: @all_dirs 308: else 309: if system then 310: @sys_dirs 311: else 312: @all_dirs - @sys_dirs 313: end 314: end 315: 316: dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact 317: end
# File ri/driver.rb, line 319 319: def populate_class_cache(class_cache, classes, extension = false) 320: classes.each do |cdesc| 321: desc = read_yaml cdesc 322: klassname = desc["full_name"] 323: 324: unless class_cache.has_key? klassname then 325: desc["display_name"] = "Class" 326: desc["sources"] = [cdesc] 327: desc["instance_method_extensions"] = [] 328: desc["class_method_extensions"] = [] 329: class_cache[klassname] = desc 330: else 331: klass = class_cache[klassname] 332: 333: if extension then 334: desc["instance_method_extensions"] = desc.delete "instance_methods" 335: desc["class_method_extensions"] = desc.delete "class_methods" 336: end 337: 338: klass.merge_enums desc 339: klass["sources"] << cdesc 340: end 341: end 342: end
# File ri/driver.rb, line 344 344: def read_yaml(path) 345: YAML.load File.read(path).gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI|SM).*/, '') 346: end
# File ri/driver.rb, line 348 348: def run 349: if @names.empty? then 350: @display.list_known_classes class_cache.keys.sort 351: else 352: @names.each do |name| 353: case name 354: when /::|\#|\./ then 355: if class_cache.key? name then 356: display_class name 357: else 358: meth = nil 359: 360: parts = name.split(/::|\#|\./) 361: meth = parts.pop unless parts.last =~ /^[A-Z]/ 362: klass = parts.join '::' 363: 364: cache = load_cache_for klass 365: # HACK Does not support F.n 366: abort "Nothing known about #{name}" unless cache 367: method = cache[name.gsub(/\./, '#')] 368: abort "Nothing known about #{name}" unless method 369: @display.display_method_info method 370: end 371: else 372: if class_cache.key? name then 373: display_class name 374: else 375: methods = select_methods(/^#{name}/) 376: if methods.size == 0 377: abort "Nothing known about #{name}" 378: elsif methods.size == 1 379: @display.display_method_info methods.first 380: else 381: @display.display_method_list methods 382: end 383: end 384: end 385: end 386: end 387: end
# File ri/driver.rb, line 389 389: def select_methods(pattern) 390: methods = [] 391: class_cache.keys.sort.each do |klass| 392: class_cache[klass]["instance_methods"].map{|h|h["name"]}.grep(pattern) do |name| 393: method = load_cache_for(klass)[klass+'#'+name] 394: methods << method if method 395: end 396: class_cache[klass]["class_methods"].map{|h|h["name"]}.grep(pattern) do |name| 397: method = load_cache_for(klass)[klass+'::'+name] 398: methods << method if method 399: end 400: end 401: methods 402: end