class RDoc::ClassModule

ClassModule is the base class for objects representing either a class or a module.

Attributes

A hash of { location => [comments] } documenting this class/module. Use add_comment to add comments.

Ruby hashes maintain insertion order, so comments render in the order they were first added. Each location maps to an array of comments, allowing a class reopened in the same file to accumulate multiple comments.

Before marshalling:

After unmarshalling:

  • location is a filename String

  • comments are RDoc::Markup::Documents

Constants that are aliases for this class or module

Class or module this constant is an alias for

Public Class Methods

Return a RDoc::ClassModule of class class_type that is a copy of module module. Used to promote modules to classes.

# File lib/rdoc/code_object/class_module.rb, line 62
def self.from_module(class_type, mod)
  klass = class_type.new mod.name

  mod.comment_location.each do |location, comments|
    comments.each { |comment| klass.add_comment comment, location }
  end

  klass.parent = mod.parent
  klass.section = mod.section

  klass.attributes.concat mod.attributes
  klass.method_list.concat mod.method_list
  klass.aliases.concat mod.aliases
  klass.external_aliases.concat mod.external_aliases
  klass.constants.concat mod.constants
  klass.includes.concat mod.includes
  klass.extends.concat mod.extends

  klass.methods_hash.update mod.methods_hash
  klass.constants_hash.update mod.constants_hash

  klass.current_section = mod.current_section
  klass.in_files.concat mod.in_files
  klass.sections.concat mod.sections
  klass.unmatched_alias_lists = mod.unmatched_alias_lists
  klass.current_section = mod.current_section
  klass.classes_hash.update mod.classes_hash
  klass.modules_hash.update mod.modules_hash
  klass.metadata.update mod.metadata

  klass.document_self = mod.received_nodoc ? nil : mod.document_self
  klass.document_children = mod.document_children
  klass.force_documentation = mod.force_documentation
  klass.done_documenting = mod.done_documenting

  # update the parent of all children

  (klass.attributes +
   klass.method_list +
   klass.aliases +
   klass.external_aliases +
   klass.constants +
   klass.includes +
   klass.extends +
   klass.classes +
   klass.modules).each do |obj|
    obj.parent = klass
    obj.full_name = nil
  end

  klass
end

Creates a new ClassModule with name with optional superclass

This is a constructor for subclasses, and must never be called directly.

Calls superclass method RDoc::Context::new
# File lib/rdoc/code_object/class_module.rb, line 120
def initialize(name, superclass = nil)
  @constant_aliases = []
  @is_alias_for     = nil
  @name             = name
  @superclass       = superclass
  @comment_location = {} # Hash of { location => [comments] }

  super()
end

Public Instance Methods

Adds comment to this ClassModule’s list of comments at location. This method is preferred over comment= since it allows ri data to be updated across multiple runs.

# File lib/rdoc/code_object/class_module.rb, line 135
def add_comment(comment, location)
  return unless document_self

  original = comment

  comment = case comment
            when Comment
              comment.normalize
            else
              normalize_comment comment
            end

  (@comment_location[location] ||= []) << comment

  self.comment = original
end

Ancestors list for this ClassModule: the list of included modules (classes will add their superclass if any).

Returns the included classes or modules, not the includes themselves. The returned values are either String or RDoc::NormalModule instances (see RDoc::Include#module).

The values are returned in reverse order of their inclusion, which is the order suitable for searching methods/attributes in the ancestors. The superclass, if any, comes last.

# File lib/rdoc/code_object/class_module.rb, line 175
def ancestors
  included_ancestors
end
Also aliased as: direct_ancestors

HTML fragment reference for this module or class using GitHub-style

anchor format (lowercase,

replaced with -).

Examples:

Foo      -> class-foo
Foo::Bar -> class-foo-bar
# File lib/rdoc/code_object/class_module.rb, line 195
def aref
  "#{aref_prefix}-#{full_name.downcase.gsub('::', '-')}"
end

Clears the comment. Used by the Ruby parser.

# File lib/rdoc/code_object/class_module.rb, line 219
def clear_comment
  @comment = ''
end

Prepares this ClassModule for use by a generator.

See RDoc::Store#complete

# File lib/rdoc/code_object/class_module.rb, line 247
def complete(min_visibility)
  update_aliases
  remove_nodoc_children
  embed_mixins
  update_includes
  update_extends
  remove_invisible min_visibility
end

Handy wrapper for marking up this class or module’s comment

# File lib/rdoc/generator/markup.rb, line 179
def description
  markup @comment_location
end

Ancestors of this class or module only

Alias for: ancestors

Does this ClassModule or any of its methods have document_self set?

# File lib/rdoc/code_object/class_module.rb, line 259
def document_self_or_methods
  document_self || method_list.any?{ |m| m.document_self }
end

Does this class or module have a comment with content or is received_nodoc true?

# File lib/rdoc/code_object/class_module.rb, line 267
def documented?
  return true if @received_nodoc
  return false if @comment_location.empty?
  @comment_location.each_value.any? { |comments| comments.any? { |c| not c.empty? } }
end

Iterates the ancestors of this class or module for which an RDoc::ClassModule exists.

# File lib/rdoc/code_object/class_module.rb, line 277
def each_ancestor # :yields: module
  return enum_for __method__ unless block_given?

  ancestors.each do |mod|
    next if String === mod
    next if self == mod
    yield mod
  end
end
# File lib/rdoc/code_object/class_module.rb, line 938
def embed_mixins
  return unless options.embed_mixins

  includes.each do |include|
    next if String === include.module
    include.module.method_list.each do |code_object|
      add_method(prepare_to_embed(code_object))
    end
    include.module.constants.each do |code_object|
      add_constant(prepare_to_embed(code_object))
    end
    include.module.attributes.each do |code_object|
      add_attribute(prepare_to_embed(code_object))
    end
  end

  extends.each do |ext|
    next if String === ext.module
    ext.module.method_list.each do |code_object|
      add_method(prepare_to_embed(code_object, true))
    end
    ext.module.attributes.each do |code_object|
      add_attribute(prepare_to_embed(code_object, true))
    end
  end
end

Looks for a symbol in the ancestors. See Context#find_local_symbol.

# File lib/rdoc/code_object/class_module.rb, line 290
def find_ancestor_local_symbol(symbol)
  each_ancestor do |m|
    res = m.find_local_symbol(symbol)
    return res if res
  end

  nil
end

Finds a class or module with name in this namespace or its descendants

# File lib/rdoc/code_object/class_module.rb, line 302
def find_class_named(name)
  return self if full_name == name
  return self if @name == name

  @classes.values.find do |klass|
    next if klass == self
    klass.find_class_named name
  end
end

Return the fully qualified name of this class or module

# File lib/rdoc/code_object/class_module.rb, line 315
def full_name
  @full_name ||= if ClassModule === parent
                   "#{parent.full_name}::#{@name}"
                 else
                   @name
                 end
end

Return array of fully qualified nesting namespaces.

For example, if full_name is A::B::C, this method returns ["A", "A::B", "A::B::C"]

# File lib/rdoc/code_object/class_module.rb, line 335
def fully_qualified_nesting_namespaces
  return nesting_namespaces if nesting_namespaces.length < 2
  @fqns ||= nesting_namespaces.inject([]) do |list, n|
    list << (list.empty? ? n : "#{list.last}::#{n}")
  end
end

Legacy HTML fragment reference for backward compatibility. Returns the old RDoc-style anchor format.

Examples:

Foo      -> class-Foo
Foo::Bar -> class-Foo::Bar
# File lib/rdoc/code_object/class_module.rb, line 207
def legacy_aref
  "#{aref_prefix}-#{full_name}"
end

Merges class_module into this ClassModule.

The data in class_module is preferred over the receiver.

# File lib/rdoc/code_object/class_module.rb, line 479
def merge(class_module)
  @parent      = class_module.parent
  @parent_name = class_module.parent_name

  other_document = parse class_module.comment_location

  if other_document
    document = parse @comment_location

    document = document.merge other_document

    @comment = Comment.from_document(document)

    @comment_location = if document.parts.first.is_a?(Markup::Document)
                          document.parts.group_by(&:file)
                        else
                          { document.file => [document] }
                        end
  end

  cm = class_module
  other_files = cm.in_files

  merge_collections attributes, cm.attributes, other_files do |add, attr|
    if add
      add_attribute attr
    else
      @attributes.delete attr
      @methods_hash.delete attr.pretty_name
    end
  end

  merge_collections constants, cm.constants, other_files do |add, const|
    if add
      add_constant const
    else
      @constants.delete const
      @constants_hash.delete const.name
    end
  end

  merge_collections includes, cm.includes, other_files do |add, incl|
    if add
      add_include incl
    else
      @includes.delete incl
    end
  end

  @includes.uniq! # clean up

  merge_collections extends, cm.extends, other_files do |add, ext|
    if add
      add_extend ext
    else
      @extends.delete ext
    end
  end

  @extends.uniq! # clean up

  merge_collections method_list, cm.method_list, other_files do |add, meth|
    if add
      add_method meth
    else
      @method_list.delete meth
      @methods_hash.delete meth.pretty_name
    end
  end

  merge_sections cm

  self
end

Does this object represent a module?

# File lib/rdoc/code_object/class_module.rb, line 620
def module?
  false
end

Allows overriding the initial name.

Used for modules and classes that are constant aliases.

# File lib/rdoc/code_object/class_module.rb, line 629
def name=(new_name)
  @name = new_name
end

Name to use to generate the url: modules and classes that are aliases for another module or class return the name of the latter.

# File lib/rdoc/code_object/class_module.rb, line 676
def name_for_path
  is_alias_for ? is_alias_for.name_for_path : full_name
end

Return array of full_name splitted by ::.

# File lib/rdoc/code_object/class_module.rb, line 326
def nesting_namespaces
  @namespaces ||= full_name.split("::").reject(&:empty?)
end

Returns the classes and modules that are not constants aliasing another class or module. For use by formatters only (caches its result).

# File lib/rdoc/code_object/class_module.rb, line 685
def non_aliases
  @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for }
end

Parses comment_location into an RDoc::Markup::Document composed of multiple RDoc::Markup::Documents with their file set.

Calls superclass method RDoc::Text#parse
# File lib/rdoc/code_object/class_module.rb, line 637
def parse(comment_location)
  case comment_location
  when String
    super
  when Hash
    docs = comment_location.flat_map do |location, comments|
      comments.map do |comment|
        doc = super comment
        doc.file = location
        doc
      end
    end

    Markup::Document.new(*docs)
  when Comment
    doc = super comment_location.text, comment_location.format
    doc.file = comment_location.location
    doc
  when Markup::Document
    return comment_location
  else
    raise ArgumentError, "unknown comment class #{comment_location.class}"
  end
end

Path to this class or module for use with HTML generator output.

# File lib/rdoc/code_object/class_module.rb, line 665
def path
  prefix = options.class_module_path_prefix
  return http_url unless prefix
  File.join(prefix, http_url)
end

Rebuilds +@comment+ from the current +@comment_location+ entries, skipping any empty placeholders.

# File lib/rdoc/code_object/class_module.rb, line 755
def rebuild_comment_from_location
  texts = @comment_location.each_value.flat_map { |comments|
    comments.filter_map { |c| c.to_s unless c.empty? }
  }
  merged = texts.join("\n---\n")
  @comment = merged.empty? ? '' : Comment.new(merged)
end

Updates the child modules or classes of class/module parent by deleting the ones that have been removed from the documentation.

parent_hash is either parent.modules_hash or parent.classes_hash and all_hash is ::all_modules_hash or ::all_classes_hash.

# File lib/rdoc/code_object/class_module.rb, line 697
def remove_nodoc_children
  prefix = self.full_name + '::'

  modules_hash.each_key do |name|
    full_name = prefix + 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 @store.classes_hash[full_name]
  end
end

Search record used by RDoc::Generator::JsonIndex

TODO: Remove this method after dropping the darkfish theme and JsonIndex generator. Use search_snippet instead for getting documentation snippets.

# File lib/rdoc/code_object/class_module.rb, line 729
def search_record
  [
    name,
    full_name,
    full_name,
    '',
    path,
    '',
    snippet(@comment_location),
  ]
end

Returns an HTML snippet of the first comment for search results.

# File lib/rdoc/code_object/class_module.rb, line 744
def search_snippet
  first_comment = @comment_location.each_value.first&.first
  return '' unless first_comment && !first_comment.empty?

  snippet(first_comment)
end

Sets the store for this class or module and its contained code objects.

Calls superclass method RDoc::CodeObject#store=
# File lib/rdoc/code_object/class_module.rb, line 766
def store=(store)
  super

  @attributes .each do |attr|  attr.store  = store end
  @constants  .each do |const| const.store = store end
  @includes   .each do |incl|  incl.store  = store end
  @extends    .each do |ext|   ext.store   = store end
  @method_list.each do |meth|  meth.store  = store end
end

Get all super classes of this class in an array. The last element might be a string if the name is unknown.

# File lib/rdoc/code_object/class_module.rb, line 809
def super_classes
  result = []
  # Degenerate input can produce a cyclic superclass chain
  visited = [full_name]
  parent = self
  while parent = parent.superclass
    if parent.is_a?(String)
      result << parent
      break
    end
    break if visited.include?(parent.full_name)
    visited << parent.full_name
    result << parent
  end
  result
end

Get the superclass of this class. Attempts to retrieve the superclass object, returns the name if it is not known.

# File lib/rdoc/code_object/class_module.rb, line 780
def superclass
  @store.find_class_named(@superclass) || @superclass
end

Set the superclass of this class to superclass

where superclass is one of:

  • nil

  • a String containing the full name of the superclass

  • the RDoc::ClassModule representing the superclass

# File lib/rdoc/code_object/class_module.rb, line 793
def superclass=(superclass)
  raise NoMethodError, "#{full_name} is a module" if module?
  case superclass
  when ClassModule
    @superclass = superclass.full_name
  when nil, String
    @superclass = superclass
  else
    raise TypeError, "superclass must be a String or RDoc::ClassModule, not #{superclass.class}"
  end
end

‘module’ or ‘class’

# File lib/rdoc/code_object/class_module.rb, line 837
def type
  module? ? 'module' : 'class'
end

Updates the child modules & classes by replacing the ones that are aliases through a constant.

The aliased module/class is replaced in the children and in RDoc::Store#modules_hash or RDoc::Store#classes_hash by a copy that has RDoc::ClassModule#is_alias_for set to the aliased module/class, and this copy is added to aliases of the aliased module/class.

Formatters can use the non_aliases method to retrieve children that are not aliases, for instance to list the namespace content, since the aliased modules are included in the constants of the class/module, that are listed separately.

# File lib/rdoc/code_object/class_module.rb, line 856
def update_aliases
  constants.each do |const|
    cm = const.is_alias_for
    cm ||= const.resolved_alias_target if const.is_a?(Constant)
    next unless cm

    # Resolve chained aliases (A = B = C) to the real class/module.
    cm = @store.find_class_or_module(cm.full_name) || cm
    while (target = cm.is_alias_for)
      cm = target
    end

    cm_alias = cm.dup
    cm_alias.name = const.name

    if full_name == 'Object'
      # Don't move top-level aliases under Object, they look ugly there
      cm_alias.parent = top_level
    else
      cm_alias.parent = self
    end
    cm_alias.full_name = nil # force update for new parent

    # Don't clobber a real (non-alias) class/module already living at this
    # name. Mirrors the BasicObject = BlankSlate guard in
    # Context#add_module_alias. Existing alias copies (set by
    # add_module_alias or a previous update_aliases pass) carry is_alias_for,
    # so they're still overwritable here.
    existing = @store.find_class_or_module(cm_alias.full_name)
    next if existing && !existing.is_alias_for

    # Persist a lazy-resolved target so Stats#report_constants and
    # Constant#marshal_dump observe the alias relationship. Skipped
    # aliases (above) intentionally leave the constant unmarked.
    const.is_alias_for ||= cm

    cm_alias.aliases.clear
    cm_alias.is_alias_for = cm

    if cm.module?
      @store.modules_hash[cm_alias.full_name] = cm_alias
      modules_hash[const.name] = cm_alias
    else
      @store.classes_hash[cm_alias.full_name] = cm_alias
      classes_hash[const.name] = cm_alias
    end

    cm.aliases << cm_alias
  end
end

Deletes from extends those whose module has been removed from the documentation.

# File lib/rdoc/code_object/class_module.rb, line 928
def update_extends
  extends.reject! do |ext|
    mod = ext.module

    !(String === mod) && @store.modules_hash[mod.full_name].nil?
  end

  extends.uniq!
end

Deletes from includes those whose module has been removed from the documentation.

# File lib/rdoc/code_object/class_module.rb, line 913
def update_includes
  includes.reject! do |include|
    mod = include.module
    !(String === mod) && @store.modules_hash[mod.full_name].nil?
  end

  includes.uniq!
end