Browse files

Move global parse state to RDoc::Store

RDoc::RI::Store has been replaced with RDoc::Store.  An alias still
exists so loading of ri data from previous versions of RDoc will still
work.

Global parse state on the RDoc::TopLevel class has been moved to
RDoc::Store instances.  Parsing now builds atop a single store instance,
but multiple parse runs cannot be run concurrently at this time as other
global state still exists.

Functionality previously existing on RDoc::TopLevel now exists as
RDoc::Store instance methods.  The store instance is provided in all
contexts RDoc::TopLevel was previously used.
  • Loading branch information...
1 parent 8d34bb5 commit 2d884240bd98c2f26cd0a41caf424d800a34af20 @drbrain drbrain committed Aug 8, 2012
Showing with 1,068 additions and 1,004 deletions.
  1. +12 −0 History.rdoc
  2. +2 −0 TODO.rdoc
  3. +1 −0 lib/rdoc.rb
  4. +9 −9 lib/rdoc/class_module.rb
  5. +6 −0 lib/rdoc/code_object.rb
  6. +24 −20 lib/rdoc/context.rb
  7. +2 −1 lib/rdoc/cross_reference.rb
  8. +3 −3 lib/rdoc/extend.rb
  9. +6 −1 lib/rdoc/generator.rb
  10. +8 −2 lib/rdoc/generator/darkfish.rb
  11. +3 −2 lib/rdoc/generator/json_index.rb
  12. +6 −7 lib/rdoc/generator/ri.rb
  13. +1 −1 lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
  14. +3 −3 lib/rdoc/include.rb
  15. +1 −1 lib/rdoc/method_attr.rb
  16. +1 −0 lib/rdoc/parser.rb
  17. +4 −4 lib/rdoc/parser/ruby.rb
  18. +14 −5 lib/rdoc/rdoc.rb
  19. +2 −353 lib/rdoc/ri/store.rb
  20. +13 −12 lib/rdoc/stats.rb
  21. +596 −0 lib/rdoc/store.rb
  22. +7 −0 lib/rdoc/test_case.rb
  23. +8 −261 lib/rdoc/top_level.rb
  24. +5 −0 test/test_rdoc_any_method.rb
  25. +43 −28 test/test_rdoc_class_module.rb
  26. +4 −4 test/test_rdoc_context.rb
  27. +1 −0 test/test_rdoc_extend.rb
  28. +3 −6 test/test_rdoc_generator_darkfish.rb
  29. +1 −3 test/test_rdoc_generator_json_index.rb
  30. +2 −0 test/test_rdoc_generator_markup.rb
  31. +7 −5 test/test_rdoc_generator_ri.rb
  32. +1 −0 test/test_rdoc_include.rb
  33. +10 −10 test/test_rdoc_normal_class.rb
  34. +1 −1 test/test_rdoc_parser_c.rb
  35. +1 −1 test/test_rdoc_parser_markdown.rb
  36. +1 −1 test/test_rdoc_parser_rd.rb
  37. +51 −61 test/test_rdoc_parser_ruby.rb
  38. +1 −1 test/test_rdoc_parser_simple.rb
  39. +15 −8 test/test_rdoc_rdoc.rb
  40. +54 −52 test/test_rdoc_ri_driver.rb
  41. +24 −24 test/test_rdoc_stats.rb
  42. +97 −10 test/{test_rdoc_ri_store.rb → test_rdoc_store.rb}
  43. +8 −96 test/test_rdoc_top_level.rb
  44. +6 −8 test/xref_test_case.rb
View
12 History.rdoc
@@ -1,12 +1,24 @@
=== 4.0
* Breaking changes
+ * RDoc::RI::Store is now RDoc::Store so ri data generated by RDoc 4 cannot
+ be read by earlier versions of RDoc. RDoc::RI::Store exists as an alias
+ of RDoc::Store so ri data from older versions can still be read.
+ RDoc::RI::Store will be removed in RDoc 5.
+
+ Tests that create RDoc::CodeObjects on the fly without wiring them into
+ the documentation tree (did not use add_class, add_method, etc.) will need
+ to be updated to use these methods. The documentation tree automatically
+ attaches them to the store instance which allows lookups to work
+ correctly.
* The default output encoding for RDoc is now UTF-8. Previously RDoc used
the default external encoding which was determined from your locale.
Issue #106 by Justin Baker.
* Removed RDoc::RI::Paths::SYSDIR and ::SITEDIR. These were hidden
constants so no breakage is expected. Use RDoc::RI::Paths::system_dir
and ::site_dir instead.
+ * RDoc generators must accept an RDoc::Store and an RDoc::Options in
+ initialize.
* Minor enhancements
* Added Markdown as a supported format. The markdown format can be set on a
View
2 TODO.rdoc
@@ -11,6 +11,8 @@ Blockers:
* Restore backwards compatibility due to paragraph text joining from existing
ri files
* Fix consumption of , after link like: RDoc[rdoc-ref:RDoc], <- comma here
+* Don't pass top_level instances to #generate.
+* Move modsort thing to RDoc::Store
Nice to have:
View
1 lib/rdoc.rb
@@ -141,6 +141,7 @@ def self.load_yaml
autoload :Parser, 'rdoc/parser'
autoload :RI, 'rdoc/ri'
autoload :Stats, 'rdoc/stats'
+ autoload :Store, 'rdoc/store'
autoload :Task, 'rdoc/task'
autoload :Text, 'rdoc/text'
View
18 lib/rdoc/class_module.rb
@@ -183,7 +183,7 @@ def comment= comment
##
# Prepares this ClassModule for use by a generator.
#
- # See RDoc::TopLevel::complete
+ # See RDoc::Store#complete
def complete min_visibility
update_aliases
@@ -538,12 +538,12 @@ def remove_nodoc_children
modules_hash.each_key do |name|
full_name = prefix + name
- modules_hash.delete name unless RDoc::TopLevel.all_modules_hash[full_name]
+ modules_hash.delete name unless @store.modules_hash[full_name]
end
classes_hash.each_key do |name|
full_name = prefix + name
- classes_hash.delete name unless RDoc::TopLevel.all_classes_hash[full_name]
+ classes_hash.delete name unless @store.classes_hash[full_name]
end
end
@@ -567,7 +567,7 @@ def search_record
# object, returns the name if it is not known.
def superclass
- RDoc::TopLevel.find_class_named(@superclass) || @superclass
+ @store.find_class_named(@superclass) || @superclass
end
##
@@ -598,7 +598,7 @@ def type
# aliases through a constant.
#
# The aliased module/class is replaced in the children and in
- # RDoc::TopLevel::all_modules_hash or RDoc::TopLevel::all_classes_hash
+ # RDoc::Store#modules_hash or RDoc::Store#classes_hash
# by a copy that has <tt>RDoc::ClassModule#is_alias_for</tt> set to
# the aliased module/class, and this copy is added to <tt>#aliases</tt>
# of the aliased module/class.
@@ -619,10 +619,10 @@ def update_aliases
cm_alias.is_alias_for = cm
if cm.module? then
- RDoc::TopLevel.all_modules_hash[cm_alias.full_name] = cm_alias
+ @store.modules_hash[cm_alias.full_name] = cm_alias
modules_hash[const.name] = cm_alias
else
- RDoc::TopLevel.all_classes_hash[cm_alias.full_name] = cm_alias
+ @store.classes_hash[cm_alias.full_name] = cm_alias
classes_hash[const.name] = cm_alias
end
@@ -639,7 +639,7 @@ def update_aliases
def update_includes
includes.reject! do |include|
mod = include.module
- !(String === mod) && RDoc::TopLevel.all_modules_hash[mod.full_name].nil?
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
end
includes.uniq!
@@ -655,7 +655,7 @@ def update_extends
extends.reject! do |ext|
mod = ext.module
- !(String === mod) && RDoc::TopLevel.all_modules_hash[mod.full_name].nil?
+ !(String === mod) && @store.modules_hash[mod.full_name].nil?
end
extends.uniq!
View
6 lib/rdoc/code_object.rb
@@ -90,6 +90,11 @@ class RDoc::CodeObject
attr_accessor :section
##
+ # The RDoc::Store for this object.
+
+ attr_accessor :store
+
+ ##
# We are the model of the code, but we know that at some point we will be
# worked on by viewers. By implementing the Viewable protocol, viewers can
# associated themselves with these objects.
@@ -105,6 +110,7 @@ def initialize
@parent = nil
@file = nil
@full_name = nil
+ @store = nil
@document_children = true
@document_self = true
View
44 lib/rdoc/context.rb
@@ -266,12 +266,12 @@ def add_class class_type, given_name, superclass = '::Object'
if full_name =~ /^(.+)::(\w+)$/ then
name = $2
ename = $1
- enclosing = RDoc::TopLevel.classes_hash[ename] ||
- RDoc::TopLevel.modules_hash[ename]
+ enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
# HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
unless enclosing then
# try the given name at top level (will work for the above example)
- enclosing = RDoc::TopLevel.classes_hash[given_name] || RDoc::TopLevel.modules_hash[given_name]
+ enclosing = @store.classes_hash[given_name] ||
+ @store.modules_hash[given_name]
return enclosing if enclosing
# not found: create the parent(s)
names = ename.split('::')
@@ -310,15 +310,15 @@ def add_class class_type, given_name, superclass = '::Object'
end
# did we believe it was a module?
- mod = RDoc::TopLevel.modules_hash.delete superclass
+ mod = @store.modules_hash.delete superclass
upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
# e.g., Object < Object
superclass = nil if superclass == full_name
end
- klass = RDoc::TopLevel.classes_hash[full_name]
+ klass = @store.classes_hash[full_name]
if klass then
# if TopLevel, it may not be registered in the classes:
@@ -335,7 +335,7 @@ def add_class class_type, given_name, superclass = '::Object'
end
else
# this is a new class
- mod = RDoc::TopLevel.modules_hash.delete full_name
+ mod = @store.modules_hash.delete full_name
if mod then
klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
@@ -345,7 +345,7 @@ def add_class class_type, given_name, superclass = '::Object'
klass = class_type.new name, superclass
enclosing.add_class_or_module(klass, enclosing.classes_hash,
- RDoc::TopLevel.classes_hash)
+ @store.classes_hash)
end
end
@@ -363,6 +363,7 @@ def add_class_or_module mod, self_hash, all_hash
mod.section = current_section # TODO declaring context? something is
# wrong here...
mod.parent = self
+ mod.store = @store
unless @done_documenting then
self_hash[mod.name] = mod
@@ -450,9 +451,9 @@ def add_module(class_type, name)
return mod if mod
full_name = child_name name
- mod = RDoc::TopLevel.modules_hash[full_name] || class_type.new(name)
+ mod = @store.modules_hash[full_name] || class_type.new(name)
- add_class_or_module(mod, @modules, RDoc::TopLevel.modules_hash)
+ add_class_or_module mod, @modules, @store.modules_hash
end
##
@@ -468,13 +469,13 @@ def add_module_alias from, name, file
# see the metaprogramming in lib/active_support/basic_object.rb,
# where we already know BasicObject as a class when we find
# BasicObject = BlankSlate
- return from if RDoc::TopLevel.find_class_or_module(to_name)
+ return from if @store.find_class_or_module to_name
if from.module? then
- RDoc::TopLevel.modules_hash[to_name] = from
+ @store.modules_hash[to_name] = from
@modules[name] = from
else
- RDoc::TopLevel.classes_hash[to_name] = from
+ @store.classes_hash[to_name] = from
@classes[name] = from
end
@@ -524,9 +525,11 @@ def add_section title, comment = nil
##
# Adds +thing+ to the collection +array+
- def add_to(array, thing)
+ def add_to array, thing
array << thing if @document_self
- thing.parent = self
+
+ thing.parent = self
+ thing.store = @store
thing.section = current_section
end
@@ -773,8 +776,8 @@ def find_external_alias_named(name)
##
# Finds a file with +name+ in this context
- def find_file_named(name)
- top_level.class.find_file_named(name)
+ def find_file_named name
+ @store.find_file_named name
end
##
@@ -844,15 +847,15 @@ def find_symbol_module(symbol)
# look for a class or module 'symbol'
case symbol
when /^::/ then
- result = RDoc::TopLevel.find_class_or_module(symbol)
+ result = @store.find_class_or_module symbol
when /^(\w+):+(.+)$/
suffix = $2
top = $1
searched = self
loop do
mod = searched.find_module_named(top)
break unless mod
- result = RDoc::TopLevel.find_class_or_module(mod.full_name + '::' + suffix)
+ result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
break if result || searched.is_a?(RDoc::TopLevel)
searched = searched.parent
end
@@ -1147,10 +1150,11 @@ def upgrade_to_class mod, class_type, enclosing
enclosing.modules_hash.delete mod.name
klass = RDoc::ClassModule.from_module class_type, mod
+ klass.store = @store
# if it was there, then we keep it even if done_documenting
- RDoc::TopLevel.classes_hash[mod.full_name] = klass
- enclosing.classes_hash[mod.name] = klass
+ @store.classes_hash[mod.full_name] = klass
+ enclosing.classes_hash[mod.name] = klass
klass
end
View
3 lib/rdoc/cross_reference.rb
@@ -102,6 +102,7 @@ class RDoc::CrossReference
def initialize context
@context = context
+ @store = context.store
@seen = {}
end
@@ -146,7 +147,7 @@ def resolve name, text
end unless ref
# Try a page name
- ref = RDoc::TopLevel.page name if not ref and name =~ /^\w+$/
+ ref = @store.page name if not ref and name =~ /^\w+$/
ref = nil if RDoc::Alias === ref # external alias, can't link to it
View
6 lib/rdoc/extend.rb
@@ -74,7 +74,7 @@ def module
# search the current context
return @name unless parent
full_name = parent.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
return @name if @name =~ /^::/
@@ -84,15 +84,15 @@ def module
ext = i.module
next if String === ext
full_name = ext.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
end
# go up the hierarchy of names
up = parent.parent
while up
full_name = up.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
up = up.parent
end
View
7 lib/rdoc/generator.rb
@@ -27,7 +27,12 @@
# == Generator Instantiation
#
# After parsing, RDoc::RDoc will instantiate a generator by calling
-# #initialize with an RDoc::Options instance.
+# #initialize with an RDoc::Store instance and an RDoc::Options instance.
+#
+# The RDoc::Store instance holds documentation for parsed source code. In
+# RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading
+# a generator from RDoc 3 and earlier you should only need to replace
+# RDoc::TopLevel with the store instance.
#
# RDoc will then call #generate on the generator instance and pass in an Array
# of RDoc::TopLevel instances, each representing a parsed file. You can use
View
10 lib/rdoc/generator/darkfish.rb
@@ -80,9 +80,15 @@ class RDoc::Generator::Darkfish
attr_reader :base_dir
##
+ # The RDoc::Store that is the source of the generated content
+
+ attr_reader :store
+
+ ##
# Initialize a few instance variables before we start
- def initialize options
+ def initialize store, options
+ @store = store
@options = options
@template_dir = Pathname.new options.template_dir
@@ -165,7 +171,7 @@ def generate top_levels
@outputdir = Pathname.new(@options.op_dir).expand_path(@base_dir)
@files = top_levels.sort
- @classes = RDoc::TopLevel.all_classes_and_modules.sort
+ @classes = @store.all_classes_and_modules.sort
@methods = @classes.map { |m| m.method_list }.flatten.sort
@modsort = get_sorted_module_list(@classes)
View
5 lib/rdoc/generator/json_index.rb
@@ -88,7 +88,8 @@ class RDoc::Generator::JsonIndex
def initialize parent_generator, options
@parent_generator = parent_generator
- @options = options
+ @store = parent_generator.store
+ @options = options
@template_dir = File.expand_path '../template/json_index', __FILE__
@base_dir = @parent_generator.base_dir
@@ -112,7 +113,7 @@ def debug_msg *msg
def generate top_levels
debug_msg "Generating JSON index"
- reset top_levels.sort, RDoc::TopLevel.all_classes_and_modules.sort
+ reset top_levels.sort, @store.all_classes_and_modules.sort
index_classes
index_methods
View
13 lib/rdoc/generator/ri.rb
@@ -13,14 +13,13 @@ class RDoc::Generator::RI
##
# Set up a new ri generator
- def initialize options #:not-new:
- @options = options
+ def initialize store, options #:not-new:
+ @options = options
+ @store = store
+ @store.path = '.'
+
@old_siginfo = nil
@current = nil
-
- @store = RDoc::RI::Store.new '.'
- @store.dry_run = @options.dry_run
- @store.encoding = @options.encoding if @options.respond_to? :encoding
end
##
@@ -32,7 +31,7 @@ def generate top_levels
@store.load_cache
- RDoc::TopLevel.all_classes_and_modules.each do |klass|
+ @store.all_classes_and_modules.each do |klass|
@current = "#{klass.class}: #{klass.full_name}"
@store.save_class klass
View
2 lib/rdoc/generator/template/darkfish/table_of_contents.rhtml
@@ -47,7 +47,7 @@
<h2 id="methods">Methods</h2>
<ul>
- <% RDoc::TopLevel.all_classes_and_modules.map do |mod|
+ <% @store.all_classes_and_modules.map do |mod|
mod.method_list
end.flatten.sort.each do |method| %>
<li class="method"><a href="<%= method.path %>"><%= method.pretty_name %> &mdash; <%= method.parent.full_name %></a>
View
6 lib/rdoc/include.rb
@@ -76,7 +76,7 @@ def module
# search the current context
return @name unless parent
full_name = parent.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
return @name if @name =~ /^::/
@@ -86,15 +86,15 @@ def module
inc = i.module
next if String === inc
full_name = inc.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
end
# go up the hierarchy of names
up = parent.parent
while up
full_name = up.child_name(@name)
- @module = RDoc::TopLevel.modules_hash[full_name]
+ @module = @store.modules_hash[full_name]
return @module if @module
up = up.parent
end
View
2 lib/rdoc/method_attr.rb
@@ -149,7 +149,7 @@ def find_method_or_attribute name # :nodoc:
return nil unless parent.respond_to? :ancestors
searched = parent.ancestors
- kernel = RDoc::TopLevel.all_modules_hash['Kernel']
+ kernel = @store.modules_hash['Kernel']
searched << kernel if kernel &&
parent != kernel && !searched.include?(kernel)
View
1 lib/rdoc/parser.rb
@@ -220,6 +220,7 @@ def self.use_markup content
def initialize top_level, file_name, content, options, stats
@top_level = top_level
@top_level.parser = self.class
+ @store = @top_level.store
@file_name = file_name
@content = content
View
8 lib/rdoc/parser/ruby.rb
@@ -614,7 +614,7 @@ def parse_class container, single, tk, comment
when "self", container.name
parse_statements container, SINGLE
else
- other = RDoc::TopLevel.find_class_named name
+ other = @store.find_class_named name
unless other then
other = container.add_module RDoc::NormalModule, name
@@ -701,7 +701,7 @@ def parse_constant container, tk, comment
if nest <= 0 and TkNL === peek_tk then
mod = if rhs_name =~ /^::/ then
- RDoc::TopLevel.find_class_or_module rhs_name
+ @store.find_class_or_module rhs_name
else
container.find_module_named rhs_name
end
@@ -1095,7 +1095,7 @@ def parse_method(container, single, tk, comment)
return
when TkTRUE, TkFALSE, TkNIL then
klass_name = "#{name_t.name.capitalize}Class"
- container = RDoc::TopLevel.find_class_named klass_name
+ container = @store.find_class_named klass_name
container ||= @top_level.add_class RDoc::NormalClass, klass_name
name = name_t2.name
@@ -1408,7 +1408,7 @@ def parse_statements(container, single = NORMAL, current_method = nil,
nest += 1
when TkSUPER then
- current_method.calls_super = true
+ current_method.calls_super = true if current_method
when TkIDENTIFIER then
if nest == 1 and current_method.nil? then
View
19 lib/rdoc/rdoc.rb
@@ -59,6 +59,11 @@ class RDoc::RDoc
attr_reader :stats
##
+ # The current documentation store
+
+ attr_accessor :store
+
+ ##
# Add +klass+ that can generate output after parsing
def self.add_generator(klass)
@@ -84,7 +89,6 @@ def self.current= rdoc
# Resets all internal state
def self.reset
- RDoc::TopLevel.reset
RDoc::Parser::C.reset
RDoc::RDoc.current = nil
end
@@ -101,6 +105,7 @@ def initialize
@old_siginfo = nil
@options = nil
@stats = nil
+ @store = nil
end
##
@@ -378,7 +383,7 @@ def parse_file filename
def parse_files files
file_list = gather_files files
- @stats = RDoc::Stats.new file_list.size, @options.verbosity
+ @stats = RDoc::Stats.new @store, file_list.length, @options.verbosity
return [] if file_list.empty?
@@ -425,7 +430,8 @@ def remove_unparseable files
# current directory, so make sure you're somewhere writable before invoking.
def document options
- RDoc::RDoc.reset
+ @store = RDoc::Store.new
+
RDoc::RDoc.current = self
if RDoc::Options === options then
@@ -447,13 +453,16 @@ def document options
@last_modified = setup_output_dir @options.op_dir, @options.force_update
end
+ @store.encoding = @options.encoding if @options.respond_to? :encoding
+ @store.dry_run = @options.dry_run
+
@start_time = Time.now
file_info = parse_files @options.files
@options.default_title = "RDoc Documentation"
- RDoc::TopLevel.complete @options.visibility
+ @store.complete @options.visibility
@stats.coverage_level = @options.coverage_report
@@ -466,7 +475,7 @@ def document options
else
gen_klass = @options.generator
- @generator = gen_klass.new @options
+ @generator = gen_klass.new @store, @options
generate file_info
end
View
355 lib/rdoc/ri/store.rb
@@ -1,357 +1,6 @@
-require 'fileutils'
+module RDoc::RI
-##
-# A set of ri data.
-#
-# The store manages reading and writing ri data for a project (gem, path,
-# etc.) and maintains a cache of methods, classes and ancestors in the
-# store.
-#
-# The store maintains a #cache of its contents for faster lookup. After
-# adding items to the store it must be flushed using #save_cache. The cache
-# contains the following structures:
-#
-# @cache = {
-# :class_methods => {}, # class name => class methods
-# :instance_methods => {}, # class name => instance methods
-# :attributes => {}, # class name => attributes
-# :modules => [], # classes and modules in this store
-# :ancestors => {}, # class name => ancestor names
-# }
-#--
-# TODO need to store the list of files and prune classes
-
-class RDoc::RI::Store
-
- ##
- # If true this Store will not write any files
-
- attr_accessor :dry_run
-
- ##
- # Path this store reads or writes
-
- attr_accessor :path
-
- ##
- # Type of ri datastore this was loaded from. See RDoc::RI::Driver,
- # RDoc::RI::Paths.
-
- attr_accessor :type
-
- ##
- # The contents of the Store
-
- attr_reader :cache
-
- ##
- # The encoding of the contents in the Store
-
- attr_accessor :encoding
-
- ##
- # Creates a new Store of +type+ that will load or save to +path+
-
- def initialize path, type = nil
- @dry_run = false
- @type = type
- @path = path
- @encoding = nil
-
- @cache = {
- :ancestors => {},
- :attributes => {},
- :class_methods => {},
- :encoding => @encoding,
- :instance_methods => {},
- :modules => [],
- }
- end
-
- ##
- # Ancestors cache accessor. Maps a klass name to an Array of its ancestors
- # in this store. If Foo in this store inherits from Object, Kernel won't be
- # listed (it will be included from ruby's ri store).
-
- def ancestors
- @cache[:ancestors]
- end
-
- ##
- # Attributes cache accessor. Maps a class to an Array of its attributes.
-
- def attributes
- @cache[:attributes]
- end
-
- ##
- # Path to the cache file
-
- def cache_path
- File.join @path, 'cache.ri'
- end
-
- ##
- # Path to the ri data for +klass_name+
-
- def class_file klass_name
- name = klass_name.split('::').last
- File.join class_path(klass_name), "cdesc-#{name}.ri"
- end
-
- ##
- # Class methods cache accessor. Maps a class to an Array of its class
- # methods (not full name).
-
- def class_methods
- @cache[:class_methods]
- end
-
- ##
- # Path where data for +klass_name+ will be stored (methods or class data)
-
- def class_path klass_name
- File.join @path, *klass_name.split('::')
- end
-
- ##
- # Removes empty items and ensures item in each collection are unique and
- # sorted
-
- def clean_cache_collection collection # :nodoc:
- collection.each do |name, item|
- if item.empty? then
- collection.delete name
- else
- # HACK mongrel-1.1.5 documents its files twice
- item.uniq!
- item.sort!
- end
- end
- end
-
- ##
- # Friendly rendition of #path
-
- def friendly_path
- case type
- when :gem then
- sep = Regexp.union(*['/', File::ALT_SEPARATOR].compact)
- @path =~ /#{sep}doc#{sep}(.*?)#{sep}ri$/
- "gem #{$1}"
- when :home then '~/.ri'
- when :site then 'ruby site'
- when :system then 'ruby core'
- else @path
- end
- end
-
- def inspect # :nodoc:
- "#<%s:0x%x %s %p>" % [self.class, object_id, @path, modules.sort]
- end
-
- ##
- # Instance methods cache accessor. Maps a class to an Array of its
- # instance methods (not full name).
-
- def instance_methods
- @cache[:instance_methods]
- end
-
- ##
- # Loads cache file for this store
-
- def load_cache
- #orig_enc = @encoding
-
- open cache_path, 'rb' do |io|
- @cache = Marshal.load io.read
- end
-
- 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 diffent 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
- rescue Errno::ENOENT
- end
-
- ##
- # Loads ri data for +klass_name+
-
- def load_class klass_name
- open class_file(klass_name), 'rb' do |io|
- Marshal.load io.read
- end
- end
-
- ##
- # Loads ri data for +method_name+ in +klass_name+
-
- def load_method klass_name, method_name
- open method_file(klass_name, method_name), 'rb' do |io|
- Marshal.load io.read
- end
- end
-
- ##
- # Path to the ri data for +method_name+ in +klass_name+
-
- 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 = if ''.respond_to? :ord then
- method_name.gsub(/\W/) { "%%%02x" % $&[0].ord }
- else
- method_name.gsub(/\W/) { "%%%02x" % $&[0] }
- end
-
- File.join class_path(klass_name), "#{method_name}-#{method_type}.ri"
- end
-
- ##
- # Modules cache accessor. An Array of all the modules (and classes) in the
- # store.
-
- def modules
- @cache[:modules]
- end
-
- ##
- # Writes the cache file for this store
-
- 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[:encoding] = @encoding # this gets set twice due to assert_cache
-
- return if @dry_run
-
- marshal = Marshal.dump @cache
-
- open cache_path, 'wb' do |io|
- io.write marshal
- end
- end
-
- ##
- # Writes the ri data for +klass+
-
- 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 Errno::ENOENT
- 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
-
- attributes = klass.attributes.map do |attribute|
- "#{attribute.definition} #{attribute.name}"
- end
-
- unless attributes.empty? then
- @cache[:attributes][full_name] ||= []
- @cache[:attributes][full_name].concat attributes
- 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 }
-
- 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
- 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
-
- marshal = Marshal.dump klass
-
- open path, 'wb' do |io|
- io.write marshal
- end
- end
-
- ##
- # Writes the ri data for +method+ on +klass+
-
- 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
-
- marshal = Marshal.dump method
-
- open method_file(full_name, method.full_name), 'wb' do |io|
- io.write marshal
- end
- end
+ Store = RDoc::Store # :nodoc:
end
View
25 lib/rdoc/stats.rb
@@ -23,17 +23,18 @@ class RDoc::Stats
# Creates a new Stats that will have +num_files+. +verbosity+ defaults to 1
# which will create an RDoc::Stats::Normal outputter.
- def initialize num_files, verbosity = 1
- @files_so_far = 0
+ def initialize store, num_files, verbosity = 1
@num_files = num_files
+ @store = store
- @coverage_level = 0
- @doc_items = nil
+ @coverage_level = 0
+ @doc_items = nil
+ @files_so_far = 0
@fully_documented = false
- @num_params = 0
- @percent_doc = nil
- @start = Time.now
- @undoc_params = 0
+ @num_params = 0
+ @percent_doc = nil
+ @start = Time.now
+ @undoc_params = 0
@display = case verbosity
when 0 then Quiet.new num_files
@@ -106,9 +107,9 @@ def begin_adding
def calculate
return if @doc_items
- ucm = RDoc::TopLevel.unique_classes_and_modules
+ ucm = @store.unique_classes_and_modules
- classes = RDoc::TopLevel.unique_classes.reject { |cm| cm.full_name == 'Object' }
+ classes = @store.unique_classes.reject { |cm| cm.full_name == 'Object' }
constants = []
ucm.each { |cm| constants.concat cm.constants }
@@ -123,7 +124,7 @@ def calculate
@num_classes, @undoc_classes = doc_stats classes
@num_constants, @undoc_constants = doc_stats constants
@num_methods, @undoc_methods = doc_stats methods
- @num_modules, @undoc_modules = doc_stats RDoc::TopLevel.unique_modules
+ @num_modules, @undoc_modules = doc_stats @store.unique_modules
@num_items =
@num_attributes +
@@ -224,7 +225,7 @@ def report
return great_job if @num_items == @doc_items
end
- ucm = RDoc::TopLevel.unique_classes_and_modules
+ ucm = @store.unique_classes_and_modules
ucm.sort.each do |cm|
report << report_class_module(cm) {
View
596 lib/rdoc/store.rb
@@ -0,0 +1,596 @@
+require 'fileutils'
+
+##
+# A set of rdoc data for a single project (gem, path, etc.).
+#
+# The store manages reading and writing ri data for a project and maintains a
+# cache of methods, classes and ancestors in the store.
+#
+# The store maintains a #cache of its contents for faster lookup. After
+# adding items to the store it must be flushed using #save_cache. The cache
+# contains the following structures:
+#
+# @cache = {
+# :class_methods => {}, # class name => class methods
+# :instance_methods => {}, # class name => instance methods
+# :attributes => {}, # class name => attributes
+# :modules => [], # classes and modules in this store
+# :ancestors => {}, # class name => ancestor names
+# }
+#--
+# TODO need to store the list of files and prune classes
+
+class RDoc::Store
+
+ ##
+ # If true this Store will not write any files
+
+ attr_accessor :dry_run
+
+ ##
+ # Path this store reads or writes
+
+ attr_accessor :path
+
+ ##
+ # Type of ri datastore this was loaded from. See RDoc::RI::Driver,
+ # RDoc::RI::Paths.
+
+ attr_accessor :type
+
+ ##
+ # The contents of the Store
+
+ attr_reader :cache
+
+ ##
+ # The encoding of the contents in the Store
+
+ attr_accessor :encoding
+
+ ##
+ # Creates a new Store of +type+ that will load or save to +path+
+
+ def initialize path = nil, type = nil
+ @dry_run = false
+ @type = type
+ @path = path
+ @encoding = nil
+
+ @cache = {
+ :ancestors => {},
+ :attributes => {},
+ :class_methods => {},
+ :encoding => @encoding,
+ :instance_methods => {},
+ :modules => [],
+ }
+
+ @classes_hash = {}
+ @modules_hash = {}
+ @files_hash = {}
+
+ @unique_classes = nil
+ @unique_modules = nil
+ end
+
+ ##
+ # Returns all classes discovered by RDoc
+
+ def all_classes
+ @classes_hash.values
+ end
+
+ ##
+ # Returns all classes and modules discovered by RDoc
+
+ def all_classes_and_modules
+ @classes_hash.values + @modules_hash.values
+ end
+
+ ##
+ # Hash of all classes known to RDoc
+
+ def classes_hash
+ @classes_hash
+ end
+
+ ##
+ # All TopLevels known to RDoc
+
+ def all_files
+ @files_hash.values
+ end
+
+ ##
+ # Hash of all files known to RDoc
+
+ def files_hash
+ @files_hash
+ end
+
+ ##
+ # Returns all modules discovered by RDoc
+
+ def all_modules
+ modules_hash.values
+ end
+
+ ##
+ # Hash of all modules known to RDoc
+
+ def modules_hash
+ @modules_hash
+ end
+
+ ##
+ # Ancestors cache accessor. Maps a klass name to an Array of its ancestors
+ # in this store. If Foo in this store inherits from Object, Kernel won't be
+ # listed (it will be included from ruby's ri store).
+
+ def ancestors
+ @cache[:ancestors]
+ end
+
+ ##
+ # Attributes cache accessor. Maps a class to an Array of its attributes.
+
+ def attributes
+ @cache[:attributes]
+ end
+
+ ##
+ # Path to the cache file
+
+ def cache_path
+ File.join @path, 'cache.ri'
+ end
+
+ ##
+ # Path to the ri data for +klass_name+
+
+ def class_file klass_name
+ name = klass_name.split('::').last
+ File.join class_path(klass_name), "cdesc-#{name}.ri"
+ end
+
+ ##
+ # Class methods cache accessor. Maps a class to an Array of its class
+ # methods (not full name).
+
+ def class_methods
+ @cache[:class_methods]
+ end
+
+ ##
+ # Path where data for +klass_name+ will be stored (methods or class data)
+
+ def class_path klass_name
+ File.join @path, *klass_name.split('::')
+ end
+
+ ##
+ # Removes empty items and ensures item in each collection are unique and
+ # sorted
+
+ def clean_cache_collection collection # :nodoc:
+ collection.each do |name, item|
+ if item.empty? then
+ collection.delete name
+ else
+ # HACK mongrel-1.1.5 documents its files twice
+ item.uniq!
+ item.sort!
+ end
+ end
+ end
+
+ ##
+ # Prepares the RDoc code object tree for use by a generator.
+ #
+ # It finds unique classes/modules defined, and replaces classes/modules that
+ # are aliases for another one by a copy with RDoc::ClassModule#is_alias_for
+ # set.
+ #
+ # It updates the RDoc::ClassModule#constant_aliases attribute of "real"
+ # classes or modules.
+ #
+ # It also completely removes the classes and modules that should be removed
+ # from the documentation and the methods that have a visibility below
+ # +min_visibility+, which is the <tt>--visibility</tt> option.
+ #
+ # See also RDoc::Context#remove_from_documentation?
+
+ 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 }
+
+ remove_nodoc @classes_hash
+ remove_nodoc @modules_hash
+
+ @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
+
+ ##
+ # Finds the class with +name+ in all discovered classes
+
+ def find_class_named name
+ @classes_hash[name]
+ end
+
+ ##
+ # Finds the class with +name+ starting in namespace +from+
+
+ 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
+
+ ##
+ # Finds the class or module with +name+
+
+ def find_class_or_module name
+ name = $' if name =~ /^::/
+ @classes_hash[name] || @modules_hash[name]
+ end
+
+ ##
+ # Finds the file with +name+ in all discovered files
+
+ def find_file_named name
+ @files_hash[name]
+ end
+
+ ##
+ # Finds the module with +name+ in all discovered modules
+
+ def find_module_named name
+ @modules_hash[name]
+ end
+
+ ##
+ # Finds unique classes/modules defined in +all_hash+,
+ # and returns them as an array. Performs the alias
+ # updates in +all_hash+: see ::complete.
+ #--
+ # TODO aliases should be registered by Context#add_module_alias
+
+ 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
+
+ ##
+ # Fixes the erroneous <tt>BasicObject < Object</tt> in 1.9.
+ #
+ # Because we assumed all classes without a stated superclass
+ # inherit from Object, we have the above wrong inheritance.
+ #
+ # We fix BasicObject right away if we are running in a Ruby
+ # version >= 1.9. If not, we may be documenting 1.9 source
+ # while running under 1.8: we search the files of BasicObject
+ # for "object.c", and fix the inheritance if we find it.
+
+ def fix_basic_object_inheritance
+ basic = classes_hash['BasicObject']
+ return unless basic
+ if RUBY_VERSION >= '1.9'
+ basic.superclass = nil
+ elsif basic.in_files.any? { |f| File.basename(f.full_name) == 'object.c' }
+ basic.superclass = nil
+ end
+ end
+
+ ##
+ # Friendly rendition of #path
+
+ def friendly_path
+ case type
+ when :gem then
+ sep = Regexp.union(*['/', File::ALT_SEPARATOR].compact)
+ @path =~ /#{sep}doc#{sep}(.*?)#{sep}ri$/
+ "gem #{$1}"
+ when :home then '~/.ri'
+ when :site then 'ruby site'
+ when :system then 'ruby core'
+ else @path
+ end
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %s %p>" % [self.class, object_id, @path, modules.sort]
+ end
+
+ ##
+ # Instance methods cache accessor. Maps a class to an Array of its
+ # instance methods (not full name).
+
+ def instance_methods
+ @cache[:instance_methods]
+ end
+
+ ##
+ # Loads cache file for this store
+
+ def load_cache
+ #orig_enc = @encoding
+
+ open cache_path, 'rb' do |io|
+ @cache = Marshal.load io.read
+ end
+
+ 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 diffent 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
+ rescue Errno::ENOENT
+ end
+
+ ##
+ # Loads ri data for +klass_name+
+
+ def load_class klass_name
+ open class_file(klass_name), 'rb' do |io|
+ obj = Marshal.load io.read
+ obj.store = self
+ obj
+ end
+ end
+
+ ##
+ # Loads ri data for +method_name+ in +klass_name+
+
+ def load_method klass_name, method_name
+ open method_file(klass_name, method_name), 'rb' do |io|
+ Marshal.load io.read
+ end
+ end
+
+ ##
+ # Path to the ri data for +method_name+ in +klass_name+
+
+ 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 = if ''.respond_to? :ord then
+ method_name.gsub(/\W/) { "%%%02x" % $&[0].ord }
+ else
+ method_name.gsub(/\W/) { "%%%02x" % $&[0] }
+ end
+
+ File.join class_path(klass_name), "#{method_name}-#{method_type}.ri"
+ end
+
+ ##
+ # Modules cache accessor. An Array of all the modules (and classes) in the
+ # store.
+
+ def modules
+ @cache[:modules]
+ end
+
+ ##
+ # Returns the RDoc::TopLevel that has the given +name+
+
+ def page name
+ @files_hash.each_value.find do |file|
+ file.text? and file.page_name == name
+ end
+ end
+
+ ##
+ # Removes from +all_hash+ the contexts that are nodoc or have no content.
+ #
+ # See RDoc::Context#remove_from_documentation?
+
+ 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
+
+ ##
+ # Writes the cache file for this store
+
+ 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[:encoding] = @encoding # this gets set twice due to assert_cache
+
+ return if @dry_run
+
+ marshal = Marshal.dump @cache
+
+ open cache_path, 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Writes the ri data for +klass+
+
+ 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 Errno::ENOENT
+ 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
+
+ attributes = klass.attributes.map do |attribute|
+ "#{attribute.definition} #{attribute.name}"
+ end
+
+ unless attributes.empty? then
+ @cache[:attributes][full_name] ||= []
+ @cache[:attributes][full_name].concat attributes
+ 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 }
+
+ 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
+ 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
+
+ marshal = Marshal.dump klass
+
+ open path, 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Writes the ri data for +method+ on +klass+
+
+ 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
+
+ marshal = Marshal.dump method
+
+ open method_file(full_name, method.full_name), 'wb' do |io|
+ io.write marshal
+ end
+ end
+
+ ##
+ # Returns the unique classes discovered by RDoc.
+ #
+ # ::complete must have been called prior to using this method.
+
+ def unique_classes
+ @unique_classes
+ end
+
+ ##
+ # Returns the unique classes and modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def unique_classes_and_modules
+ @unique_classes + @unique_modules
+ end
+
+ ##
+ # Returns the unique modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def unique_modules
+ @unique_modules
+ end
+
+end
+
View
7 lib/rdoc/test_case.rb
@@ -39,6 +39,13 @@ def setup
RDoc::Markup::PreProcess.reset
@pwd = Dir.pwd
+
+ @store = RDoc::Store.new
+
+ @rdoc = RDoc::RDoc.new
+ @rdoc.store = @store
+
+ RDoc::RDoc.current = @rdoc
end
##
View
269 lib/rdoc/top_level.rb
@@ -33,276 +33,22 @@ class RDoc::TopLevel < RDoc::Context
attr_accessor :parser
##
- # Returns all classes discovered by RDoc
-
- def self.all_classes
- @all_classes_hash.values
- end
-
- ##
- # Returns all classes and modules discovered by RDoc
-
- def self.all_classes_and_modules
- @all_classes_hash.values + @all_modules_hash.values
- end
-
- ##
- # Hash of all classes known to RDoc
-
- def self.all_classes_hash
- @all_classes_hash
- end
-
- ##
- # All TopLevels known to RDoc
-
- def self.all_files
- @all_files_hash.values
- end
-
- ##
- # Hash of all files known to RDoc
-
- def self.all_files_hash
- @all_files_hash
- end
-
- ##
- # Returns all modules discovered by RDoc
-
- def self.all_modules
- all_modules_hash.values
- end
-
- ##
- # Hash of all modules known to RDoc
-
- def self.all_modules_hash
- @all_modules_hash
- end
-
- ##
- # Prepares the RDoc code object tree for use by a generator.
- #
- # It finds unique classes/modules defined, and replaces classes/modules that
- # are aliases for another one by a copy with RDoc::ClassModule#is_alias_for
- # set.
- #
- # It updates the RDoc::ClassModule#constant_aliases attribute of "real"
- # classes or modules.
- #
- # It also completely removes the classes and modules that should be removed
- # from the documentation and the methods that have a visibility below
- # +min_visibility+, which is the <tt>--visibility</tt> option.
- #
- # See also RDoc::Context#remove_from_documentation?
-
- def self.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 }
-
- remove_nodoc @all_classes_hash
- remove_nodoc @all_modules_hash
-
- @unique_classes = find_unique @all_classes_hash
- @unique_modules = find_unique @all_modules_hash
-
- unique_classes_and_modules.each do |cm|
- cm.complete min_visibility
- end
-
- @all_files_hash.each_key do |file_name|
- tl = @all_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 @all_classes_hash[name]
- else
- tl.modules_hash[name] = cm if @all_modules_hash[name]
- end
- end
- end
- end
- end
-
- ##
- # Finds the class with +name+ in all discovered classes
-
- def self.find_class_named(name)
- @all_classes_hash[name]
- end
-
- ##
- # Finds the class with +name+ starting in namespace +from+
-
- def self.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
-
- ##
- # Finds the class or module with +name+
-
- def self.find_class_or_module(name)
- name = $' if name =~ /^::/
- RDoc::TopLevel.classes_hash[name] || RDoc::TopLevel.modules_hash[name]
- end
-
- ##
- # Finds the file with +name+ in all discovered files
-
- def self.find_file_named(name)
- @all_files_hash[name]
- end
-
- ##
- # Finds the module with +name+ in all discovered modules
-
- def self.find_module_named(name)
- modules_hash[name]
- end
-
- ##
- # Finds unique classes/modules defined in +all_hash+,
- # and returns them as an array. Performs the alias
- # updates in +all_hash+: see ::complete.
- #--
- # TODO aliases should be registered by Context#add_module_alias
-
- def self.find_unique(all_hash)
- unique = []
-
- all_hash.each_pair do |full_name, cm|
- unique << cm if full_name == cm.full_name
- end
-
- unique
- end
-
- ##
- # Fixes the erroneous <tt>BasicObject < Object</tt> in 1.9.
- #
- # Because we assumed all classes without a stated superclass
- # inherit from Object, we have the above wrong inheritance.
- #
- # We fix BasicObject right away if we are running in a Ruby
- # version >= 1.9. If not, we may be documenting 1.9 source
- # while running under 1.8: we search the files of BasicObject
- # for "object.c", and fix the inheritance if we find it.
-
- def self.fix_basic_object_inheritance
- basic = all_classes_hash['BasicObject']
- return unless basic
- if RUBY_VERSION >= '1.9'
- basic.superclass = nil
- elsif basic.in_files.any? { |f| File.basename(f.full_name) == 'object.c' }
- basic.superclass = nil
- end
- end
-
- ##
# Creates a new RDoc::TopLevel with +file_name+ only if one with the same
# name does not exist in all_files.
def self.new file_name
- if top_level = @all_files_hash[file_name] then
+ store = RDoc::RDoc.current.store
+ if top_level = store.files_hash[file_name] then
top_level
else
- top_level = super
- @all_files_hash[file_name] = top_level
- top_level
- end
- end
-
- ##
- # Returns the RDoc::TopLevel that has the given +name+
-
- def self.page name
- @all_files_hash.each_value.find do |file|
- file.text? and file.page_name == name
- end
- end
-
- ##
- # Removes from +all_hash+ the contexts that are nodoc or have no content.
- #
- # See RDoc::Context#remove_from_documentation?
-
- def self.remove_nodoc(all_hash)
- all_hash.keys.each do |name|
- context = all_hash[name]
- all_hash.delete(name) if context.remove_from_documentation?
+ super
end
end
##
- # Empties RDoc of stored class, module and file information
-
- def self.reset
- @all_classes_hash = {}
- @all_modules_hash = {}
- @all_files_hash = {}
- end
-
- ##
- # Returns the unique classes discovered by RDoc.
- #
- # ::complete must have been called prior to using this method.
-
- def self.unique_classes
- @unique_classes
- end
-
- ##
- # Returns the unique classes and modules discovered by RDoc.
- # ::complete must have been called prior to using this method.
-
- def self.unique_classes_and_modules
- @unique_classes + @unique_modules
- end
-
- ##
- # Returns the unique modules discovered by RDoc.
- # ::complete must have been called prior to using this method.
-
- def self.unique_modules
- @unique_modules
- end
-
- class << self
- alias classes all_classes
- alias classes_hash all_classes_hash
-
- alias files all_files
- alias files_hash all_files_hash
-
- alias modules all_modules
- alias modules_hash all_modules_hash
- end
-
- reset
-
- ##
# Creates a new TopLevel for +file_name+
- def initialize(file_name)
+ def initialize file_name
super()
@name = nil
@relative_name = file_name
@@ -313,7 +59,8 @@ def initialize(file_name)
@classes_or_modules = []
- RDoc::TopLevel.files_hash[file_name] = self
+ @store = RDoc::RDoc.current.store
+ @store.files_hash[file_name] = self
end
##
@@ -393,7 +140,7 @@ def display?
# ones of this instance?
def find_class_or_module name
- RDoc::TopLevel.find_class_or_module name
+ @store.find_class_or_module name
end
##
@@ -457,7 +204,7 @@ def last_modified
def object_class
@object_class ||= begin
- oc = self.class.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
+ oc = @store.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
oc.record_location self
oc
end
View
5 test/test_rdoc_any_method.rb
@@ -253,9 +253,11 @@ def test_superclass_method
m1 = RDoc::AnyMethod.new '', 'supers'
c1 = RDoc::NormalClass.new 'Outer'
+ c1.store = @store
c1.add_method m1
c2 = RDoc::NormalClass.new 'Inner', c1
+ c2.store = @store
c2.add_method m2
c2.add_method m3
@@ -273,11 +275,14 @@ def test_superclass_method_multilevel
m1 = RDoc::AnyMethod.new '', 'supers'
c1 = RDoc::NormalClass.new 'Outer'
+ c1.store = @store
c1.add_method m1
c2 = RDoc::NormalClass.new 'Middle', c1
+ c2.store = @store
c3 = RDoc::NormalClass.new 'Inner', c2
+ c3.store = @store
c3.add_method m2
assert_equal m1, m2.superclass_method,
View
71 test/test_rdoc_class_module.rb
@@ -157,6 +157,7 @@ def test_marshal_dump
cm.add_comment 'this is a comment', tl
loaded = Marshal.load Marshal.dump cm
+ loaded.store = @store
assert_equal cm, loaded
@@ -216,6 +217,8 @@ def test_marshal_load_version_0
"\"\rinstance\x06;\x06F[\b[\a;\n[\x06I" \
"\"\am1\x06;\x06F[\a;\v[\x00[\a;\f[\x00"
+ loaded.store = @store
+
assert_equal cm, loaded
comment = RDoc::Markup::Document.new(
@@ -277,6 +280,8 @@ def test_marshal_load_version_1
"\"\rinstance\x06;\x06F[\b[\a;\v[\x06[\aI" \
"\"\am1\x06;\x06F@\x11[\a;\f[\x00[\a;\r[\x00"
+ loaded.store = @store
+
assert_equal cm, loaded
inner = RDoc::Markup::Document.new(
@@ -354,6 +359,8 @@ def test_marshal_load_version_2
"\"\am1\x06;\x06F@\x11[\a;\f[\x00[\a;\r[\x00" \
"[\x06[\bI\"\aE1\x06;\x06Fo;\a\a;\b[\x00;\n0@\x11"
+ loaded.store = @store
+
assert_equal cm, loaded
inner = RDoc::Markup::Document.new(
@@ -596,16 +603,17 @@ def test_merge_constants_version_0
def test_merge_extends
tl1 = RDoc::TopLevel.new 'one.rb'
- tl2 = RDoc::TopLevel.new 'two.rb'
-
- cm1 = RDoc::ClassModule.new 'Klass'
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
ext = cm1.add_extend RDoc::Extend.new('I1', 'one')
ext.record_location tl1
ext = cm1.add_extend RDoc::Extend.new('I3', 'one')
ext.record_location tl1
- cm2 = RDoc::ClassModule.new 'Klass'
+ tl2 = RDoc::TopLevel.new 'two.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
cm2.instance_variable_set :@comment, @RM::Document.new
ext = cm2.add_extend RDoc::Extend.new('I2', 'two')
@@ -630,16 +638,18 @@ def test_merge_extends
def test_merge_includes
tl1 = RDoc::TopLevel.new 'one.rb'
- tl2 = RDoc::TopLevel.new 'two.rb'
- cm1 = RDoc::ClassModule.new 'Klass'
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
incl = cm1.add_include RDoc::Include.new('I1', 'one')
incl.record_location tl1
incl = cm1.add_include RDoc::Include.new('I3', 'one')
incl.record_location tl1
- cm2 = RDoc::ClassModule.new 'Klass'
+ tl2 = RDoc::TopLevel.new 'two.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
cm2.instance_variable_set :@comment, @RM::Document.new
incl = cm2.add_include RDoc::Include.new('I2', 'two')
@@ -665,14 +675,17 @@ def test_merge_includes
def test_merge_includes_version_0
tl1 = RDoc::TopLevel.new 'one.rb'
- cm1 = RDoc::ClassModule.new 'Klass'
+ cm1 = tl1.add_class RDoc::ClassModule, 'Klass'
incl = cm1.add_include RDoc::Include.new('I1', 'one')
incl.record_location tl1
incl = cm1.add_include RDoc::Include.new('I3', 'one')
incl.record_location tl1
- cm2 = RDoc::ClassModule.new 'Klass'
+ tl2 = RDoc::TopLevel.new 'one.rb'
+ tl2.store = RDoc::Store.new
+
+ cm2 = tl2.add_class RDoc::ClassModule, 'Klass'
cm2.instance_variable_set :@comment, @RM::Document.new
incl = cm2.add_include RDoc::Include.new('I2', 'two')
@@ -820,12 +833,12 @@ def test_parse_comment_location
end
def test_remove_nodoc_children
- parent = RDoc::ClassModule.new 'A'
+ parent = @top_level.add_class RDoc::ClassModule, 'A'
parent.modules_hash.replace 'B' => true, 'C' => true
- RDoc::TopLevel.all_modules_hash.replace 'A::B' => true
+ @store.modules_hash.replace 'A::B' => true
parent.classes_hash.replace 'D' => true, 'E' => true
- RDoc::TopLevel.all_classes_hash.replace 'A::D' => true
+ @store.classes_hash.replace 'A::D' => true