class RuboCop::Cop::Style::Documentation

Checks for missing top-level documentation of classes and modules. Classes with no body are exempt from the check and so are namespace modules - modules that have nothing in their bodies except classes, other modules, constant definitions or constant visibility declarations.

The documentation requirement is annulled if the class or module has a ‘#:nodoc:` comment next to it. Likewise, `#:nodoc: all` does the same for all its children.

@example

# bad
class Person
  # ...
end

module Math
end

# good
# Description/Explanation of Person class
class Person
  # ...
end

# allowed
# Class without body
class Person
end

# Namespace - A namespace can be a class or a module
# Containing a class
module Namespace
  # Description/Explanation of Person class
  class Person
    # ...
  end
end

# Containing constant visibility declaration
module Namespace
  class Private
  end

  private_constant :Private
end

# Containing constant definition
module Namespace
  Public = Class.new
end

# Macro calls
module Namespace
  extend Foo
end

@example AllowedConstants: [‘ClassMethods’]

# good
module A
  module ClassMethods
    # ...
  end
end

Constants

MSG

Public Instance Methods

on_class(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 94
def on_class(node)
  return unless node.body

  check(node, node.body)
end
on_module(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 100
def on_module(node)
  check(node, node.body)
end

Private Instance Methods

allowed_constants() click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 174
def allowed_constants
  @allowed_constants ||= cop_config.fetch('AllowedConstants', []).map(&:intern)
end
check(node, body) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 106
def check(node, body)
  return if namespace?(body)
  return if documentation_comment?(node)
  return if constant_allowed?(node)
  return if nodoc_self_or_outer_module?(node)
  return if include_statement_only?(body)

  range = range_between(node.source_range.begin_pos, node.loc.name.end_pos)
  message = format(MSG, type: node.type, identifier: identifier(node))
  add_offense(range, message: message)
end
compact_namespace?(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 147
def compact_namespace?(node)
  node.loc.name.source.include?('::')
end
constant_allowed?(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 143
def constant_allowed?(node)
  allowed_constants.include?(node.identifier.short_name)
end
constant_declaration?(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 139
def constant_declaration?(node)
  constant_definition?(node) || constant_visibility_declaration?(node)
end
identifier(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 178
def identifier(node)
  # Get the fully qualified identifier for a class/module
  nodes = [node, *node.each_ancestor(:class, :module)]
  identifier = nodes.reverse_each.flat_map { |n| qualify_const(n.identifier) }.join('::')

  identifier.sub('::::', '::')
end
include_statement_only?(body) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 123
def include_statement_only?(body)
  return true if include_statement?(body)

  body.respond_to?(:children) && body.children.all? { |node| include_statement_only?(node) }
end
namespace?(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 129
def namespace?(node)
  return false unless node

  if node.begin_type?
    node.children.all? { |child| constant_declaration?(child) }
  else
    constant_definition?(node)
  end
end
nodoc(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 170
def nodoc(node)
  processed_source.ast_with_comments[node.children.first].first
end
nodoc?(comment, require_all: false) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 166
def nodoc?(comment, require_all: false)
  /^#\s*:nodoc:#{"\s+all\s*$" if require_all}/.match?(comment.text)
end
nodoc_comment?(node, require_all: false) click to toggle source

First checks if the :nodoc: comment is associated with the class/module. Unless the element is tagged with :nodoc:, the search proceeds to check its ancestors for :nodoc: all. Note: How end-of-line comments are associated with code changed in parser-2.2.0.4.

# File lib/rubocop/cop/style/documentation.rb, line 156
def nodoc_comment?(node, require_all: false)
  return false unless node&.children&.first

  nodoc = nodoc(node)

  return true if same_line?(nodoc, node) && nodoc?(nodoc, require_all: require_all)

  nodoc_comment?(node.parent, require_all: true)
end
nodoc_self_or_outer_module?(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 118
def nodoc_self_or_outer_module?(node)
  nodoc_comment?(node) ||
    (compact_namespace?(node) && nodoc_comment?(outer_module(node).first))
end
qualify_const(node) click to toggle source
# File lib/rubocop/cop/style/documentation.rb, line 186
def qualify_const(node)
  return if node.nil?

  if node.cbase_type? || node.self_type? || node.call_type? || node.variable?
    node.source
  else
    [qualify_const(node.namespace), node.short_name].compact
  end
end