class RDoc::Store
单个项目(gem、路径等)的一组 rdoc 数据。
该存储器管理项目的 ri 数据的读取和写入,并在存储器中维护方法、类和祖先的缓存。
存储器维护其内容的缓存
,以便更快地查找。将项目添加到存储器后,必须使用save_cache
刷新它。缓存包含以下结构
@cache = { :ancestors => {}, # class name => ancestor names :attributes => {}, # class name => attributes :class_methods => {}, # class name => class methods :instance_methods => {}, # class name => instance methods :modules => [], # classes and modules in this store :pages => [], # page names }
常量
- MarshalFilter
属性
将 C 变量映射到每个已解析的 C 文件的类或模块名称。
将 C 变量映射到每个已解析的 C 文件的单例类名称。
存储器
的内容
如果为 true,则此存储器
将不写入任何文件
存储器
中内容的编码
此存储器读取或写入的路径
此解析树的RDoc::RDoc
驱动程序。 这允许咨询文档树的类访问用户设置的选项,例如。
加载此 ri 数据存储的类型。 请参阅RDoc::RI::Driver
,RDoc::RI::Paths
。
惰性常量别名将在传递过程中被发现
公共类方法
创建 type
的新存储器
,该存储器将加载或保存到 path
# File rdoc/store.rb, line 127 def initialize path = nil, type = nil @dry_run = false @encoding = nil @path = path @rdoc = nil @type = type @cache = { :ancestors => {}, :attributes => {}, :class_methods => {}, :c_class_variables => {}, :c_singleton_class_variables => {}, :encoding => @encoding, :instance_methods => {}, :main => nil, :modules => [], :pages => [], :title => nil, } @classes_hash = {} @modules_hash = {} @files_hash = {} @text_files_hash = {} @c_enclosure_classes = {} @c_enclosure_names = {} @c_class_variables = {} @c_singleton_class_variables = {} @unique_classes = nil @unique_modules = nil @unmatched_constant_alias = {} end
公共实例方法
将 module
添加为给定 variable
的 C 文件的封闭(命名空间)。
# File rdoc/store.rb, line 169 def add_c_enclosure variable, namespace @c_enclosure_classes[variable] = namespace end
从RDoc::Parser::C
添加 C 变量
# File rdoc/store.rb, line 176 def add_c_variables c_parser filename = c_parser.top_level.relative_name @c_class_variables[filename] = make_variable_map c_parser.classes @c_singleton_class_variables[filename] = c_parser.singleton_classes end
将具有 name
的文件作为RDoc::TopLevel
添加到存储器。返回创建的RDoc::TopLevel
。
# File rdoc/store.rb, line 188 def add_file absolute_name, relative_name: absolute_name, parser: nil unless top_level = @files_hash[relative_name] then top_level = RDoc::TopLevel.new absolute_name, relative_name top_level.parser = parser if parser top_level.store = self @files_hash[relative_name] = top_level @text_files_hash[relative_name] = top_level if top_level.text? end top_level end
返回RDoc
发现的所有类
# File rdoc/store.rb, line 224 def all_classes @classes_hash.values end
返回RDoc
发现的所有类和模块
# File rdoc/store.rb, line 231 def all_classes_and_modules @classes_hash.values + @modules_hash.values end
RDoc
已知的所有 TopLevel
# File rdoc/store.rb, line 238 def all_files @files_hash.values end
返回RDoc
发现的所有模块
# File rdoc/store.rb, line 245 def all_modules modules_hash.values end
祖先缓存访问器。 将 klass 名称映射到此存储器中其祖先的数组。如果此存储器中的 Foo 继承自Object
,则不会列出 Kernel(它将包含在 ruby 的 ri 存储器中)。
# File rdoc/store.rb, line 254 def ancestors @cache[:ancestors] end
属性缓存访问器。 将一个类映射到其属性的数组。
# File rdoc/store.rb, line 261 def attributes @cache[:attributes] end
缓存文件的路径
# File rdoc/store.rb, line 268 def cache_path File.join @path, 'cache.ri' end
klass_name
的 ri 数据的路径
# File rdoc/store.rb, line 275 def class_file klass_name name = klass_name.split('::').last File.join class_path(klass_name), "cdesc-#{name}.ri" end
类方法缓存访问器。 将一个类映射到其类方法数组(不是全名)。
# File rdoc/store.rb, line 284 def class_methods @cache[:class_methods] end
将存储 klass_name
的数据(方法或类数据)的路径
# File rdoc/store.rb, line 291 def class_path klass_name File.join @path, *klass_name.split('::') end
RDoc
已知的所有类的哈希
# File rdoc/store.rb, line 298 def classes_hash @classes_hash end
准备RDoc
代码对象树以供生成器使用。
它会查找定义的唯一类/模块,并用设置了RDoc::ClassModule#is_alias_for
的副本替换作为另一个别名的类/模块。
它更新“真实”类或模块的RDoc::ClassModule#constant_aliases
属性。
它还会完全删除应从文档中删除的类和模块,以及可见性低于 min_visibility
的方法,min_visibility
是 --visibility
选项。
另请参阅RDoc::Context#remove_from_documentation?
# File rdoc/store.rb, line 334 def complete min_visibility fix_basic_object_inheritance # cache included modules before they are removed from the documentation all_classes_and_modules.each { |cm| cm.ancestors } unless min_visibility == :nodoc then remove_nodoc @classes_hash remove_nodoc @modules_hash end @unique_classes = find_unique @classes_hash @unique_modules = find_unique @modules_hash unique_classes_and_modules.each do |cm| cm.complete min_visibility end @files_hash.each_key do |file_name| tl = @files_hash[file_name] unless tl.text? then tl.modules_hash.clear tl.classes_hash.clear tl.classes_or_modules.each do |cm| name = cm.full_name if cm.type == 'class' then tl.classes_hash[name] = cm if @classes_hash[name] else tl.modules_hash[name] = cm if @modules_hash[name] end end end end end
RDoc
已知的所有文件的哈希
# File rdoc/store.rb, line 374 def files_hash @files_hash end
查找给定 C variable
的封闭(命名空间)。
# File rdoc/store.rb, line 381 def find_c_enclosure variable @c_enclosure_classes.fetch variable do break unless name = @c_enclosure_names[variable] mod = find_class_or_module name unless mod then loaded_mod = load_class_data name file = loaded_mod.in_files.first return unless file # legacy data source file.store = self mod = file.add_module RDoc::NormalModule, name end @c_enclosure_classes[variable] = mod end end
查找所有已发现的类中具有 name
的类
# File rdoc/store.rb, line 406 def find_class_named name @classes_hash[name] end
查找从命名空间 from
开始的具有 name
的类
# File rdoc/store.rb, line 413 def find_class_named_from name, from from = find_class_named from unless RDoc::Context === from until RDoc::TopLevel === from do return nil unless from klass = from.find_class_named name return klass if klass from = from.parent end find_class_named name end
查找具有 name
的类或模块
# File rdoc/store.rb, line 431 def find_class_or_module name name = $' if name =~ /^::/ @classes_hash[name] || @modules_hash[name] end
查找所有已发现的文件中具有 name
的文件
# File rdoc/store.rb, line 439 def find_file_named name @files_hash[name] end
查找所有已发现的模块中具有 name
的模块
# File rdoc/store.rb, line 446 def find_module_named name @modules_hash[name] end
返回作为文本文件并具有给定 file_name
的RDoc::TopLevel
# File rdoc/store.rb, line 454 def find_text_page file_name @text_files_hash.each_value.find do |file| file.full_name == file_name end end
查找 all_hash
中定义的唯一类/模块,并将它们作为数组返回。在 all_hash
中执行别名更新:请参阅 ::complete。
# File rdoc/store.rb, line 467 def find_unique all_hash unique = [] all_hash.each_pair do |full_name, cm| unique << cm if full_name == cm.full_name end unique end
修复 1.9 中的错误 BasicObject < Object
。
因为我们假设所有没有声明超类的类都继承自Object
,所以我们有上述错误的继承。
如果我们在 Ruby 版本 >= 1.9 中运行,我们会立即修复 BasicObject。
# File rdoc/store.rb, line 486 def fix_basic_object_inheritance basic = classes_hash['BasicObject'] return unless basic basic.superclass = nil end
path
的友好表示
# File rdoc/store.rb, line 495 def friendly_path case type when :gem then parent = File.expand_path '..', @path "gem #{File.basename parent}" when :home then RDoc.home when :site then 'ruby site' when :system then 'ruby core' else @path end end
实例方法缓存访问器。 将一个类映射到其实例方法的数组(不是全名)。
# File rdoc/store.rb, line 515 def instance_methods @cache[:instance_methods] end
将此存储器中的所有项加载到内存中。这将重新创建供生成器使用的文档树
# File rdoc/store.rb, line 523 def load_all load_cache module_names.each do |module_name| mod = find_class_or_module(module_name) || load_class(module_name) # load method documentation since the loaded class/module does not have # it loaded_methods = mod.method_list.map do |method| load_method module_name, method.full_name end mod.method_list.replace loaded_methods loaded_attributes = mod.attributes.map do |attribute| load_method module_name, attribute.full_name end mod.attributes.replace loaded_attributes end all_classes_and_modules.each do |mod| descendent_re = /^#{mod.full_name}::[^:]+$/ module_names.each do |name| next unless name =~ descendent_re descendent = find_class_or_module name case descendent when RDoc::NormalClass then mod.classes_hash[name] = descendent when RDoc::NormalModule then mod.modules_hash[name] = descendent end end end @cache[:pages].each do |page_name| page = load_page page_name @files_hash[page_name] = page @text_files_hash[page_name] = page if page.text? end end
为此存储器加载缓存文件
# File rdoc/store.rb, line 571 def load_cache #orig_enc = @encoding @cache = marshal_load(cache_path) load_enc = @cache[:encoding] # TODO this feature will be time-consuming to add: # a) Encodings may be incompatible but transcodeable # b) Need to warn in the appropriate spots, wherever they may be # c) Need to handle cross-cache differences in encodings # d) Need to warn when generating into a cache with different encodings # #if orig_enc and load_enc != orig_enc then # warn "Cached encoding #{load_enc} is incompatible with #{orig_enc}\n" \ # "from #{path}/cache.ri" unless # Encoding.compatible? orig_enc, load_enc #end @encoding = load_enc unless @encoding @cache[:pages] ||= [] @cache[:main] ||= nil @cache[:c_class_variables] ||= {} @cache[:c_singleton_class_variables] ||= {} @cache[:c_class_variables].each do |_, map| map.each do |variable, name| @c_enclosure_names[variable] = name end end @cache rescue Errno::ENOENT end
加载 klass_name
的 ri 数据,并将其连接到此存储器。
# File rdoc/store.rb, line 610 def load_class klass_name obj = load_class_data klass_name obj.store = self case obj when RDoc::NormalClass then @classes_hash[klass_name] = obj when RDoc::SingleClass then @classes_hash[klass_name] = obj when RDoc::NormalModule then @modules_hash[klass_name] = obj end end
加载 klass_name
的 ri 数据
# File rdoc/store.rb, line 628 def load_class_data klass_name file = class_file klass_name marshal_load(file) rescue Errno::ENOENT => e error = MissingFileError.new(self, file, klass_name) error.set_backtrace e.backtrace raise error end
在 klass_name
中加载 method_name
的 ri 数据
# File rdoc/store.rb, line 641 def load_method klass_name, method_name file = method_file klass_name, method_name obj = marshal_load(file) obj.store = self obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name) obj rescue Errno::ENOENT => e error = MissingFileError.new(self, file, klass_name + method_name) error.set_backtrace e.backtrace raise error end
加载 page_name
的 ri 数据
# File rdoc/store.rb, line 657 def load_page page_name file = page_file page_name obj = marshal_load(file) obj.store = self obj rescue Errno::ENOENT => e error = MissingFileError.new(self, file, page_name) error.set_backtrace e.backtrace raise error end
设置此RDoc
存储器的主要页面。
# File rdoc/store.rb, line 680 def main= page @cache[:main] = page end
将 C 解析器中的变量 => ClassModule 映射 variables
转换为变量 => 类名称映射。
# File rdoc/store.rb, line 688 def make_variable_map variables map = {} variables.each { |variable, class_module| map[variable] = class_module.full_name } map end
klass_name
中 method_name
的 ri 数据的路径
# File rdoc/store.rb, line 701 def method_file klass_name, method_name method_name = method_name.split('::').last method_name =~ /#(.*)/ method_type = $1 ? 'i' : 'c' method_name = $1 if $1 method_name = method_name.gsub(/\W/) { "%%%02x" % $&[0].ord } File.join class_path(klass_name), "#{method_name}-#{method_type}.ri" end
模块缓存访问器。 存储器中所有模块(和类)名称的数组。
# File rdoc/store.rb, line 715 def module_names @cache[:modules] end
RDoc
已知的所有模块的哈希
# File rdoc/store.rb, line 722 def modules_hash @modules_hash end
返回作为文本文件并具有给定 name
的RDoc::TopLevel
# File rdoc/store.rb, line 729 def page name @text_files_hash.each_value.find do |file| file.page_name == name or file.base_name == name end end
page_name
的 ri 数据的路径
# File rdoc/store.rb, line 738 def page_file page_name file_name = File.basename(page_name).gsub('.', '_') File.join @path, File.dirname(page_name), "page-#{file_name}.ri" end
从 all_hash
中移除那些标记为 nodoc 或没有内容的上下文。
请参阅 RDoc::Context#remove_from_documentation?
# File rdoc/store.rb, line 749 def remove_nodoc all_hash all_hash.keys.each do |name| context = all_hash[name] all_hash.delete(name) if context.remove_from_documentation? end end
确保对 C 变量名的任何引用都被解析为对应的类。
# File rdoc/store.rb, line 204 def resolve_c_superclasses @classes_hash.each_value do |klass| if klass.superclass.is_a?(String) && (candidate = find_c_enclosure(klass.superclass)) klass.superclass = candidate end end end
保存存储中的所有条目
# File rdoc/store.rb, line 759 def save load_cache all_classes_and_modules.each do |klass| save_class klass klass.each_method do |method| save_method klass, method end klass.each_attribute do |attribute| save_method klass, attribute end end all_files.each do |file| save_page file end save_cache end
为这个存储写入缓存文件
# File rdoc/store.rb, line 784 def save_cache clean_cache_collection @cache[:ancestors] clean_cache_collection @cache[:attributes] clean_cache_collection @cache[:class_methods] clean_cache_collection @cache[:instance_methods] @cache[:modules].uniq! @cache[:modules].sort! @cache[:pages].uniq! @cache[:pages].sort! @cache[:encoding] = @encoding # this gets set twice due to assert_cache @cache[:c_class_variables].merge! @c_class_variables @cache[:c_singleton_class_variables].merge! @c_singleton_class_variables return if @dry_run File.open cache_path, 'wb' do |io| Marshal.dump @cache, io end end
为 klass
(或模块) 写入 ri 数据
# File rdoc/store.rb, line 811 def save_class klass full_name = klass.full_name FileUtils.mkdir_p class_path(full_name) unless @dry_run @cache[:modules] << full_name path = class_file full_name begin disk_klass = load_class full_name klass = disk_klass.merge klass rescue MissingFileError end # BasicObject has no ancestors ancestors = klass.direct_ancestors.compact.map do |ancestor| # HACK for classes we don't know about (class X < RuntimeError) String === ancestor ? ancestor : ancestor.full_name end @cache[:ancestors][full_name] ||= [] @cache[:ancestors][full_name].concat ancestors attribute_definitions = klass.attributes.map do |attribute| "#{attribute.definition} #{attribute.name}" end unless attribute_definitions.empty? then @cache[:attributes][full_name] ||= [] @cache[:attributes][full_name].concat attribute_definitions end to_delete = [] unless klass.method_list.empty? then @cache[:class_methods][full_name] ||= [] @cache[:instance_methods][full_name] ||= [] class_methods, instance_methods = klass.method_list.partition { |meth| meth.singleton } class_methods = class_methods. map { |method| method.name } instance_methods = instance_methods.map { |method| method.name } attribute_names = klass.attributes.map { |attr| attr.name } old = @cache[:class_methods][full_name] - class_methods to_delete.concat old.map { |method| method_file full_name, "#{full_name}::#{method}" } old = @cache[:instance_methods][full_name] - instance_methods - attribute_names to_delete.concat old.map { |method| method_file full_name, "#{full_name}##{method}" } @cache[:class_methods][full_name] = class_methods @cache[:instance_methods][full_name] = instance_methods end return if @dry_run FileUtils.rm_f to_delete File.open path, 'wb' do |io| Marshal.dump klass, io end end
为 klass
上的 method
写入 ri 数据
# File rdoc/store.rb, line 885 def save_method klass, method full_name = klass.full_name FileUtils.mkdir_p class_path(full_name) unless @dry_run cache = if method.singleton then @cache[:class_methods] else @cache[:instance_methods] end cache[full_name] ||= [] cache[full_name] << method.name return if @dry_run File.open method_file(full_name, method.full_name), 'wb' do |io| Marshal.dump method, io end end
为 page
写入 ri 数据
# File rdoc/store.rb, line 908 def save_page page return unless page.text? path = page_file page.full_name FileUtils.mkdir_p File.dirname(path) unless @dry_run cache[:pages] ||= [] cache[:pages] << page.full_name return if @dry_run File.open path, 'wb' do |io| Marshal.dump page, io end end
此存储内容来源。
对于来自 gem 的存储,来源是 gem 的名称。 对于来自主目录的存储,来源是 “home”。 对于系统 ri 存储(标准库文档),来源是 “ruby”。 对于来自站点 ri 目录的存储,存储的来源是 “site”。 对于其他存储,来源是 path
。
# File rdoc/store.rb, line 934 def source case type when :gem then File.basename File.expand_path '..', @path when :home then 'home' when :site then 'site' when :system then 'ruby' else @path end end
为此 RDoc
存储设置标题页面。
# File rdoc/store.rb, line 955 def title= title @cache[:title] = title end
返回 RDoc
发现的唯一类。
::complete 必须在使用此方法之前被调用。
# File rdoc/store.rb, line 964 def unique_classes @unique_classes end
返回 RDoc
发现的唯一类和模块。 ::complete 必须在使用此方法之前被调用。
# File rdoc/store.rb, line 972 def unique_classes_and_modules @unique_classes + @unique_modules end
返回 RDoc
发现的唯一模块。 ::complete 必须在使用此方法之前被调用。
# File rdoc/store.rb, line 980 def unique_modules @unique_modules end
设置 absolute_name
的解析器,除非它来自源代码文件。
# File rdoc/store.rb, line 215 def update_parser_of_file(absolute_name, parser) if top_level = @files_hash[absolute_name] then @text_files_hash[absolute_name] = top_level if top_level.text? end end
私有实例方法
# File rdoc/store.rb, line 985 def marshal_load(file) File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)} end