| 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