class RDoc::RI::Driver
RI 驱动程序实现了命令行 ri 工具。
该驱动程序支持
-
从以下位置加载 RI 数据
-
Ruby 的标准库
-
RubyGems
-
~/.rdoc
-
用户提供的目录
-
-
分页输出(使用 RI_PAGER 环境变量、PAGER 环境变量或 less、more 和 pager 程序)
-
带有制表符补全的交互模式
-
缩写名称(ri Zl 显示 Zlib 文档)
-
彩色输出
-
合并来自多个 RI 数据源的输出
常量
- RDOC_REFS_REGEXP
属性
显示类或模块之后的所有方法文档
RI 路径中每个条目的 RDoc::RI::Store
控制分页器与 $stdout 的用户
公共类方法
ri 的默认选项
# File rdoc/ri/driver.rb, line 77 def self.default_options options = {} options[:interactive] = false options[:profile] = false options[:show_all] = false options[:expand_refs] = true options[:use_stdout] = !$stdout.tty? options[:width] = 72 # By default all standard paths are used. options[:use_system] = true options[:use_site] = true options[:use_home] = true options[:use_gems] = true options[:extra_doc_dirs] = [] return options end
使用 pp 转储 data_path
# File rdoc/ri/driver.rb, line 99 def self.dump data_path require 'pp' File.open data_path, 'rb' do |io| pp Marshal.load(io.read) end end
使用来自 ::process_args
的 initial_options
创建一个新的驱动程序
# File rdoc/ri/driver.rb, line 402 def initialize initial_options = {} @paging = false @classes = nil options = self.class.default_options.update(initial_options) @formatter_klass = options[:formatter] require 'profile' if options[:profile] @names = options[:names] @list = options[:list] @doc_dirs = [] @stores = [] RDoc::RI::Paths.each(options[:use_system], options[:use_site], options[:use_home], options[:use_gems], *options[:extra_doc_dirs]) do |path, type| @doc_dirs << path store = RDoc::RI::Store.new path, type store.load_cache @stores << store end @list_doc_dirs = options[:list_doc_dirs] @interactive = options[:interactive] @server = options[:server] @use_stdout = options[:use_stdout] @show_all = options[:show_all] @width = options[:width] @expand_refs = options[:expand_refs] end
解析 argv
并返回选项的哈希
# File rdoc/ri/driver.rb, line 110 def self.process_args argv options = default_options opts = OptionParser.new do |opt| opt.program_name = File.basename $0 opt.version = RDoc::VERSION opt.release = nil opt.summary_indent = ' ' * 4 opt.banner = <<-EOT Usage: #{opt.program_name} [options] [name ...] Where name can be: Class | Module | Module::Class Class::method | Class#method | Class.method | method gem_name: | gem_name:README | gem_name:History ruby: | ruby:NEWS | ruby:globals All class names may be abbreviated to their minimum unambiguous form. If a name is ambiguous, all valid options will be listed. A '.' matches either class or instance methods, while #method matches only instance and ::method matches only class methods. README and other files may be displayed by prefixing them with the gem name they're contained in. If the gem name is followed by a ':' all files in the gem will be shown. The file name extension may be omitted where it is unambiguous. 'ruby' can be used as a pseudo gem name to display files from the Ruby core documentation. Use 'ruby:' by itself to get a list of all available core documentation files. For example: #{opt.program_name} Fil #{opt.program_name} File #{opt.program_name} File.new #{opt.program_name} zip #{opt.program_name} rdoc:README #{opt.program_name} ruby:comments Note that shell quoting or escaping may be required for method names containing punctuation: #{opt.program_name} 'Array.[]' #{opt.program_name} compact\\! To see the default directories #{opt.program_name} will search, run: #{opt.program_name} --list-doc-dirs Specifying the --system, --site, --home, --gems, or --doc-dir options will limit ri to searching only the specified directories. ri options may be set in the RI environment variable. The ri pager can be set with the RI_PAGER environment variable or the PAGER environment variable. EOT opt.separator nil opt.separator "Options:" opt.separator nil opt.on("--[no-]interactive", "-i", "In interactive mode you can repeatedly", "look up methods with autocomplete.") do |interactive| options[:interactive] = interactive end opt.separator nil opt.on("--[no-]all", "-a", "Show all documentation for a class or", "module.") do |show_all| options[:show_all] = show_all end opt.separator nil opt.on("--[no-]list", "-l", "List classes ri knows about.") do |list| options[:list] = list end opt.separator nil opt.on("--[no-]pager", "Send output to a pager,", "rather than directly to stdout.") do |use_pager| options[:use_stdout] = !use_pager end opt.separator nil opt.on("-T", "Synonym for --no-pager.") do options[:use_stdout] = true end opt.separator nil opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger, "Set the width of the output.") do |width| options[:width] = width end opt.separator nil opt.on("--server[=PORT]", Integer, "Run RDoc server on the given port.", "The default port is 8214.") do |port| options[:server] = port || 8214 end opt.separator nil formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort formatters = formatters.sort.map do |formatter| formatter.to_s.sub('To', '').downcase end formatters -= %w[html label test] # remove useless output formats opt.on("--format=NAME", "-f", "Use the selected formatter. The default", "formatter is bs for paged output and ansi", "otherwise. Valid formatters are:", "#{formatters.join(', ')}.", formatters) do |value| options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}" end opt.separator nil opt.on("--[no-]expand-refs", "Expand rdoc-refs at the end of output") do |value| options[:expand_refs] = value end opt.separator nil opt.on("--help", "-h", "Show help and exit.") do puts opts exit end opt.separator nil opt.on("--version", "-v", "Output version information and exit.") do puts "#{opts.program_name} #{opts.version}" exit end opt.separator nil opt.separator "Data source options:" opt.separator nil opt.on("--[no-]list-doc-dirs", "List the directories from which ri will", "source documentation on stdout and exit.") do |list_doc_dirs| options[:list_doc_dirs] = list_doc_dirs end opt.separator nil opt.on("--doc-dir=DIRNAME", "-d", Array, "List of directories from which to source", "documentation in addition to the standard", "directories. May be repeated.") do |value| value.each do |dir| unless File.directory? dir then raise OptionParser::InvalidArgument, "#{dir} is not a directory" end options[:extra_doc_dirs] << File.expand_path(dir) end end opt.separator nil opt.on("--no-standard-docs", "Do not include documentation from", "the Ruby standard library, site_lib,", "installed gems, or ~/.rdoc.", "Use with --doc-dir.") do options[:use_system] = false options[:use_site] = false options[:use_gems] = false options[:use_home] = false end opt.separator nil opt.on("--[no-]system", "Include documentation from Ruby's", "standard library. Defaults to true.") do |value| options[:use_system] = value end opt.separator nil opt.on("--[no-]site", "Include documentation from libraries", "installed in site_lib.", "Defaults to true.") do |value| options[:use_site] = value end opt.separator nil opt.on("--[no-]gems", "Include documentation from RubyGems.", "Defaults to true.") do |value| options[:use_gems] = value end opt.separator nil opt.on("--[no-]home", "Include documentation stored in ~/.rdoc.", "Defaults to true.") do |value| options[:use_home] = value end opt.separator nil opt.separator "Debug options:" opt.separator nil opt.on("--[no-]profile", "Run with the ruby profiler.") do |value| options[:profile] = value end opt.separator nil opt.on("--dump=CACHE", "Dump data from an ri cache or data file.") do |value| unless File.readable?(value) abort "#{value.inspect} is not readable" end if File.directory?(value) abort "#{value.inspect} is a directory" end options[:dump_path] = File.new(value) end end argv = ENV['RI'].to_s.split(' ').concat argv opts.parse! argv options[:names] = argv options[:use_stdout] ||= !$stdout.tty? options[:use_stdout] ||= options[:interactive] options[:width] ||= 72 options rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e puts opts puts puts e exit 1 end
使用 argv
运行 ri 命令行可执行文件
# File rdoc/ri/driver.rb, line 387 def self.run argv = ARGV options = process_args argv if options[:dump_path] then dump options[:dump_path] return end ri = new options ri.run end
公共实例方法
将未文档化的类 also_in
的路径添加到 out
# File rdoc/ri/driver.rb, line 441 def add_also_in out, also_in return if also_in.empty? out << RDoc::Markup::Rule.new(1) out << RDoc::Markup::Paragraph.new("Also found in:") paths = RDoc::Markup::Verbatim.new also_in.each do |store| paths.parts.push store.friendly_path, "\n" end out << paths end
为在 classes
中描述的类 name
向 out
添加类标题。
# File rdoc/ri/driver.rb, line 458 def add_class out, name, classes heading = if classes.all? { |klass| klass.module? } then name else superclass = classes.map do |klass| klass.superclass unless klass.module? end.compact.shift || 'Object' superclass = superclass.full_name unless String === superclass "#{name} < #{superclass}" end out << RDoc::Markup::Heading.new(1, heading) out << RDoc::Markup::BlankLine.new end
将 extends
添加到 out
# File rdoc/ri/driver.rb, line 485 def add_extends out, extends add_extension_modules out, 'Extended by', extends end
将给定 type
的此模块的 extensions
列表添加到 out
。add_includes
和 add_extends
调用此方法,因此您应该直接使用它们。
# File rdoc/ri/driver.rb, line 493 def add_extension_modules out, type, extensions return if extensions.empty? out << RDoc::Markup::Rule.new(1) out << RDoc::Markup::Heading.new(1, "#{type}:") extensions.each do |modules, store| if modules.length == 1 then add_extension_modules_single out, store, modules.first else add_extension_modules_multiple out, store, modules end end end
为 store
向 out
添加“(from …)”
# File rdoc/ri/driver.rb, line 478 def add_from out, store out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})") end
将 includes
添加到 out
# File rdoc/ri/driver.rb, line 552 def add_includes out, includes add_extension_modules out, 'Includes', includes end
查找方法 name
并将其添加到 out
# File rdoc/ri/driver.rb, line 559 def add_method out, name filtered = lookup_method name method_document out, name, filtered end
将 klass
中所有方法的文档添加到 out
# File rdoc/ri/driver.rb, line 567 def add_method_documentation out, klass klass.method_list.each do |method| begin add_method out, method.full_name rescue NotFoundError next end end end
将 methods
列表添加到 out
,标题为 name
# File rdoc/ri/driver.rb, line 580 def add_method_list out, methods, name return if methods.empty? out << RDoc::Markup::Heading.new(1, "#{name}:") out << RDoc::Markup::BlankLine.new if @use_stdout and !@interactive then out.concat methods.map { |method| RDoc::Markup::Verbatim.new method } else out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', ')) end out << RDoc::Markup::BlankLine.new end
返回 klass
的祖先类
# File rdoc/ri/driver.rb, line 600 def ancestors_of klass ancestors = [] unexamined = [klass] seen = [] loop do break if unexamined.empty? current = unexamined.shift seen << current stores = classes[current] next unless stores and not stores.empty? klasses = stores.flat_map do |store| store.ancestors[current] || [] end.uniq klasses = klasses - seen ancestors.concat klasses unexamined.concat klasses end ancestors.reverse end
从 found
、klasess
和 includes
构建 RDoc::Markup::Document
# File rdoc/ri/driver.rb, line 637 def class_document name, found, klasses, includes, extends also_in = [] out = RDoc::Markup::Document.new add_class out, name, klasses add_includes out, includes add_extends out, extends found.each do |store, klass| render_class out, store, klass, also_in end add_also_in out, also_in expand_rdoc_refs_at_the_bottom(out) out end
将已知类或模块映射到可以从中加载的存储的哈希
# File rdoc/ri/driver.rb, line 704 def classes return @classes if @classes @classes = {} @stores.each do |store| store.cache[:modules].each do |mod| # using default block causes searched-for modules to be added @classes[mod] ||= [] @classes[mod] << store end end @classes end
返回在其中找到 name
的存储以及与之匹配的类、扩展和包含项
# File rdoc/ri/driver.rb, line 724 def classes_and_includes_and_extends_for name klasses = [] extends = [] includes = [] found = @stores.map do |store| begin klass = store.load_class name klasses << klass extends << [klass.extends, store] if klass.extends includes << [klass.includes, store] if klass.includes [store, klass] rescue RDoc::Store::MissingFileError end end.compact extends.reject! do |modules,| modules.empty? end includes.reject! do |modules,| modules.empty? end [found, klasses, includes, extends] end
根据缓存完成 name
。用于 Readline
# File rdoc/ri/driver.rb, line 749 def complete name completions = [] klass, selector, method = parse_name name complete_klass name, klass, selector, method, completions complete_method name, klass, selector, completions completions.sort.uniq end
将 document
转换为文本并将其写入分页器
# File rdoc/ri/driver.rb, line 799 def display document page do |io| f = formatter(io) f.width = @width if @width and f.respond_to?(:width) text = document.accept f io.write text end end
输出类 name
的格式化 RI 数据。对未文档化的类进行分组
# File rdoc/ri/driver.rb, line 812 def display_class name return if name =~ /#|\./ found, klasses, includes, extends = classes_and_includes_and_extends_for name return if found.empty? out = class_document name, found, klasses, includes, extends display out end
输出方法 name
的格式化 RI 数据
# File rdoc/ri/driver.rb, line 828 def display_method name out = RDoc::Markup::Document.new add_method out, name expand_rdoc_refs_at_the_bottom(out) display out end
输出类或方法 name
的格式化 RI 数据。
如果找到 name
返回 true,如果不是则猜测替代项返回 false,如果无法猜测 name
则引发错误。
# File rdoc/ri/driver.rb, line 844 def display_name name if name =~ /\w:(\w|$)/ then display_page name return true end return true if display_class name display_method name if name =~ /::|#|\./ true rescue NotFoundError matches = list_methods_matching name if name =~ /::|#|\./ matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty? raise if matches.empty? page do |io| io.puts "#{name} not found, maybe you meant:" io.puts io.puts matches.sort.join("\n") end false end
显示 name
中的每个名称
# File rdoc/ri/driver.rb, line 873 def display_names names names.each do |name| name = expand_name name display_name name end end
输出页面 name
的格式化 RI 数据。
# File rdoc/ri/driver.rb, line 884 def display_page name store_name, page_name = name.split ':', 2 store = @stores.find { |s| s.source == store_name } return display_page_list store if page_name.empty? pages = store.cache[:pages] unless pages.include? page_name then found_names = pages.select do |n| n =~ /#{Regexp.escape page_name}\.[^.]+$/ end if found_names.length.zero? then return display_page_list store, pages elsif found_names.length > 1 then return display_page_list store, found_names, page_name end page_name = found_names.first end page = store.load_page page_name display page.comment end
为 store
中的页面输出格式化的 RI 页面列表。
# File rdoc/ri/driver.rb, line 915 def display_page_list store, pages = store.cache[:pages], search = nil out = RDoc::Markup::Document.new title = if search then "#{search} pages" else 'Pages' end out << RDoc::Markup::Heading.new(1, "#{title} in #{store.friendly_path}") out << RDoc::Markup::BlankLine.new list = RDoc::Markup::List.new(:BULLET) pages.each do |page| list << RDoc::Markup::Paragraph.new(page) end out << list display out end
将缩写的 klass klass
扩展为完全限定的类。“Zl::Da” 将扩展为 Zlib::DataError。
# File rdoc/ri/driver.rb, line 959 def expand_class klass class_names = classes.keys ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z")) if ary.length != 1 && ary.first != klass if check_did_you_mean suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass) } raise NotFoundError.new(klass, suggestion_proc) else raise NotFoundError, klass end end ary.first end
将 name
的类部分扩展为完全限定的类。请参阅 expand_class
。
# File rdoc/ri/driver.rb, line 977 def expand_name name klass, selector, method = parse_name name return [selector, method].join if klass.empty? case selector when ':' then [find_store(klass), selector, method] else [expand_class(klass), selector, method] end.join end
# File rdoc/ri/driver.rb, line 1525 def expand_rdoc_refs_at_the_bottom(out) return unless @expand_refs extracted_rdoc_refs = [] out.each do |part| content = if part.respond_to?(:text) part.text else next end rdoc_refs = content.scan(RDOC_REFS_REGEXP).uniq.map do |file_name, _anchor| file_name end extracted_rdoc_refs.concat(rdoc_refs) end found_pages = extracted_rdoc_refs.map do |ref| begin @stores.first.load_page(ref) rescue RDoc::Store::MissingFileError end end.compact found_pages.each do |page| out << RDoc::Markup::Heading.new(4, "Expanded from #{page.full_name}") out << RDoc::Markup::BlankLine.new out << page.comment end end
过滤 found
中的方法,尝试查找与 name
匹配的方法。
# File rdoc/ri/driver.rb, line 993 def filter_methods found, name regexp = name_regexp name filtered = found.find_all do |store, methods| methods.any? { |method| method.full_name =~ regexp } end return filtered unless filtered.empty? found end
生成与 name
匹配的项目,包括找到它们的存储,正在搜索的类,找到它们的类(祖先),要查找的方法类型(来自 method_type
)和正在搜索的方法名称
# File rdoc/ri/driver.rb, line 1011 def find_methods name klass, selector, method = parse_name name types = method_type selector klasses = nil ambiguous = klass.empty? if ambiguous then klasses = classes.keys else klasses = ancestors_of klass klasses.unshift klass end methods = [] klasses.each do |ancestor| ancestors = classes[ancestor] next unless ancestors klass = ancestor if ambiguous ancestors.each do |store| methods << [store, klass, ancestor, types, method] end end methods = methods.sort_by do |_, k, a, _, m| [k, a, m].compact end methods.each do |item| yield(*item) # :yields: store, klass, ancestor, types, method end self end
查找与 name
匹配的存储,它可以是 gem 的名称、“ruby”、“home” 或 “site”。
另请参阅 RDoc::Store#source
# File rdoc/ri/driver.rb, line 1057 def find_store name @stores.each do |store| source = store.source return source if source == name return source if store.type == :gem and source =~ /^#{Regexp.escape name}-\d/ end raise RDoc::RI::Driver::NotFoundError, name end
创建一个新的 RDoc::Markup::Formatter
。如果使用 -f 给出了格式化程序,则使用它。如果要输出到分页器,则使用 bs,否则使用 ansi。
# File rdoc/ri/driver.rb, line 1074 def formatter(io) if @formatter_klass then @formatter_klass.new elsif paging? or !io.tty? then RDoc::Markup::ToBs.new else RDoc::Markup::ToAnsi.new end end
如果 Readline 可用,则使用它以交互方式运行 ri。
# File rdoc/ri/driver.rb, line 1087 def interactive puts "\nEnter the method name you want to look up." begin require 'readline' rescue LoadError end if defined? Readline then Readline.completion_proc = method :complete puts "You can use tab to autocomplete." end puts "Enter a blank line to exit.\n\n" loop do name = if defined? Readline then Readline.readline ">> ", true else print ">> " $stdin.gets end return if name.nil? or name.empty? begin display_name expand_name(name.strip) rescue NotFoundError => e puts e.message end end rescue Interrupt exit end
列出 ri 已知的以 names
开头的类。如果 names
为空,则显示所有已知类。
# File rdoc/ri/driver.rb, line 1126 def list_known_classes names = [] classes = [] stores.each do |store| classes << store.module_names end classes = classes.flatten.uniq.sort unless names.empty? then filter = Regexp.union names.map { |name| /^#{name}/ } classes = classes.grep filter end page do |io| if paging? or io.tty? then if names.empty? then io.puts "Classes and Modules known to ri:" else io.puts "Classes and Modules starting with #{names.join ', '}:" end io.puts end io.puts classes.join("\n") end end
返回与 name
匹配的方法数组
# File rdoc/ri/driver.rb, line 1158 def list_methods_matching name found = [] find_methods name do |store, klass, ancestor, types, method| if types == :instance or types == :both then methods = store.instance_methods[ancestor] if methods then matches = methods.grep(/^#{Regexp.escape method.to_s}/) matches = matches.map do |match| "#{klass}##{match}" end found.concat matches end end if types == :class or types == :both then methods = store.class_methods[ancestor] next unless methods matches = methods.grep(/^#{Regexp.escape method.to_s}/) matches = matches.map do |match| "#{klass}::#{match}" end found.concat matches end end found.uniq end
从 store
加载 klass
上方法 name
的 RI 数据。type
和 cache
指示它是类方法还是实例方法。
# File rdoc/ri/driver.rb, line 1197 def load_method store, cache, klass, type, name methods = store.public_send(cache)[klass] return unless methods method = methods.find do |method_name| method_name == name end return unless method store.load_method klass, "#{type}#{method}" rescue RDoc::Store::MissingFileError => e comment = RDoc::Comment.new("missing documentation at #{e.file}").parse method = RDoc::AnyMethod.new nil, name method.comment = comment method end
返回与 name
匹配的方法的 RI 数据数组
# File rdoc/ri/driver.rb, line 1220 def load_methods_matching name found = [] find_methods name do |store, klass, ancestor, types, method| methods = [] methods << load_method(store, :class_methods, ancestor, '::', method) if [:class, :both].include? types methods << load_method(store, :instance_methods, ancestor, '#', method) if [:instance, :both].include? types found << [store, methods.compact] end found.reject do |path, methods| methods.empty? end end
返回与 name
匹配的经过筛选的方法列表
# File rdoc/ri/driver.rb, line 1241 def lookup_method name found = load_methods_matching name if found.empty? if check_did_you_mean methods = [] _, _, method_name = parse_name name find_methods name do |store, klass, ancestor, types, method| methods.push(*store.class_methods[klass]) if [:class, :both].include? types methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types end methods = methods.uniq suggestion_proc = -> { DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name) } raise NotFoundError.new(name, suggestion_proc) else raise NotFoundError, name end end filter_methods found, name end
从 found
、klasses
和 includes
构建 RDoc::Markup::Document
# File rdoc/ri/driver.rb, line 1266 def method_document out, name, filtered out << RDoc::Markup::Heading.new(1, name) out << RDoc::Markup::BlankLine.new filtered.each do |store, methods| methods.each do |method| render_method out, store, method, name end end out end
返回 selector
的方法类型(:both、:instance、:class)
# File rdoc/ri/driver.rb, line 1282 def method_type selector case selector when '.', nil then :both when '#' then :instance else :class end end
返回 name
的正则表达式,该正则表达式将匹配 RDoc::AnyMethod
的名称。
# File rdoc/ri/driver.rb, line 1294 def name_regexp name klass, type, name = parse_name name case type when '#', '::' then /^#{klass}#{type}#{Regexp.escape name}$/ else /^#{klass}(#|::)#{Regexp.escape name}$/ end end
通过分页程序分页输出。
# File rdoc/ri/driver.rb, line 1308 def page if pager = setup_pager then begin yield pager ensure pager.close end else yield $stdout end rescue Errno::EPIPE ensure @paging = false end
我们正在使用分页器吗?
# File rdoc/ri/driver.rb, line 1326 def paging? @paging end
从像 Foo::Bar#baz 这样的 name
中提取类、选择器和方法名称部分。
注意:给定 Foo::Bar,即使 Bar 可能是一个方法,也被视为一个类。
# File rdoc/ri/driver.rb, line 1337 def parse_name name parts = name.split(/(::?|#|\.)/) if parts.length == 1 then if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then type = '.' meth = parts.pop else type = nil meth = nil end elsif parts.length == 2 or parts.last =~ /::|#|\./ then type = parts.pop meth = nil elsif parts[1] == ':' then klass = parts.shift type = parts.shift meth = parts.join elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/ then meth = parts.pop type = parts.pop end klass ||= parts.join [klass, type, meth] end
根据给定的选项查找并显示 ri 数据。
# File rdoc/ri/driver.rb, line 1458 def run if @list_doc_dirs then puts @doc_dirs elsif @list then list_known_classes @names elsif @server then start_server elsif @interactive or @names.empty? then interactive else display_names @names end rescue NotFoundError => e abort e.message end
设置一个分页程序来传递输出。依次尝试 RI_PAGER 和 PAGER 环境变量,然后是 pager、less,最后是 more。
# File rdoc/ri/driver.rb, line 1478 def setup_pager return if @use_stdout pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more'] require 'shellwords' pagers.compact.uniq.each do |pager| pager = Shellwords.split(pager) next if pager.empty? io = IO.popen(pager, 'w') rescue next next if $? and $?.pid == io.pid and $?.exited? # pager didn't work @paging = true return io end @use_stdout = true nil end
为 ri 启动一个 WEBrick 服务器。
# File rdoc/ri/driver.rb, line 1504 def start_server begin require 'webrick' rescue LoadError abort "webrick is not found. You may need to `gem install webrick` to install webrick." end server = WEBrick::HTTPServer.new :Port => @server extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact server.mount '/', RDoc::Servlet, nil, extra_doc_dirs trap 'INT' do server.shutdown end trap 'TERM' do server.shutdown end server.start end