Class RDoc::RI::Driver
In: ri/driver.rb
Parent: Object

Methods

Public Class methods

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

Public Instance methods

[Source]

     # File ri/driver.rb, line 252
252:   def cache_file_for(klassname)
253:     File.join cache_file_path, klassname.gsub(/:+/, "-")
254:   end

[Source]

     # File ri/driver.rb, line 256
256:   def cache_file_path
257:     File.join @homepath, 'cache'
258:   end

[Source]

     # 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

[Source]

     # File ri/driver.rb, line 248
248:   def class_cache_file_path
249:     File.join cache_file_path, @class_cache_name
250:   end

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # File ri/driver.rb, line 404
404:   def write_cache(cache, path)
405:     File.open path, "wb" do |cache_file|
406:       Marshal.dump cache, cache_file
407:     end
408: 
409:     cache
410:   end

[Validate]